We are migrating from WAS 8.5 (which uses openJPA for JPA 2.0, Hibernate 4.2.x [last JPA 2.0 version]) to WAS 9.0 (which uses eclipseLink for JPA 2.1 and Hibernate 5.2.18 [last JPA 2.1 version]) and except one thing the migration was successfull.
To make it possible to deploy the same application multiple times on a server and run against different databases we definied a variable DataSource
reference in the persistence.xml
:
<persistence-unit name="App_DB" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:comp/env/jdbc/app/dataSourceRef</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle12cDialect" />
<property name="connection.autocommit" value="false" />
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.WebSphereExtendedJTATransactionLookup" />
<property name="jta.UserTransaction" value="java:comp/UserTransaction" />
<property name="hibernate.hbm2ddl.auto" value="validate" />
<property name="javax.persistence.validation.mode" value="none" />
</properties>
</persistence-unit>
As you see the <jta-data-source>
is a reference. The refrence is binded in the ibm-ejb-jar-bnd.xml
<session name="StartUpService">
<resource-ref name="jdbc/app/dataSourceRef" binding-name="jdbc/app/appDB"/>
</session>
and used in the class as folloing:
@Singleton
@Startup
@LocalBean
public class StartUpService {
@Resource(name = "jdbc/app/dataSourceRef", type = DataSource.class)
DataSource dataSource;
}
This worked all fine for WAS 8.5 / openJPA . But when using WAS 9.0 / eclipseLink this no longer works and procudes the following exceptions when you try to start the application:
[WebContainer : 4] ERROR org.hibernate.hql.spi.id.IdTableHelper - Unable to use JDBC Connection to create Statement java.sql.SQLException: Unsupported use of GenericConnection. A GenericConnection is provided during application start when creating an EntityManagerFactory for a persistence unit which has configured one of its datasource to be in the component naming context; java:comp/env. During application start, the component naming context will not exist, and the correct datasource cannot be determined. When the persistence unit is used, the proper datasource and connection will be obtained and used. at com.ibm.ws.jpa.management.GenericConnection.unsupportedUseSQLException(GenericConnection.java:636) ~[com.ibm.ws.runtime.jar:?] at com.ibm.ws.jpa.management.GenericConnection.createStatement(GenericConnection.java:144) ~[com.ibm.ws.runtime.jar:?] at org.hibernate.hql.spi.id.IdTableHelper.executeIdTableCreationStatements(IdTableHelper.java:77) [org.hibernate-hibernate-core-5.2.18.Final.jar:5.2.18.Final] at org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy.finishPreparation(GlobalTemporaryTableBulkIdStrategy.java:125) [org.hibernate-hibernate-core-5.2.18.Final.jar:5.2.18.Final] at org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy.finishPreparation(GlobalTemporaryTableBulkIdStrategy.java:42) [org.hibernate-hibernate-core-5.2.18.Final.jar:5.2.18.Final] at org.hibernate.hql.spi.id.AbstractMultiTableBulkIdStrategyImpl.prepare(AbstractMultiTableBulkIdStrategyImpl.java:88) [org.hibernate-hibernate-core-5.2.18.Final.jar:5.2.18.Final] at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:305) [org.hibernate-hibernate-core-5.2.18.Final.jar:5.2.18.Final] at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:462) [org.hibernate-hibernate-core-5.2.18.Final.jar:5.2.18.Final] at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:945) [org.hibernate-hibernate-core-5.2.18.Final.jar:5.2.18.Final] at org.hibernate.jpa.HibernatePersistenceProvider.createContainerEntityManagerFactory(HibernatePersistenceProvider.java:151) [org.hibernate-hibernate-core-5.2.18.Final.jar:5.2.18.Final] at com.ibm.ws.jpa.management.JPAPUnitInfo.createEMFactory(JPAPUnitInfo.java:1161) [com.ibm.ws.runtime.jar:?]
[...]
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1909) [com.ibm.ws.runtime.jar:?]
[WebContainer : 4] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Unsupported use of GenericConnection. A GenericConnection is provided during application start when creating an EntityManagerFactory for a persistence unit which has configured one of its datasource to be in the component naming context; java:comp/env. During application start, the component naming context will not exist, and the correct datasource cannot be determined. When the persistence unit is used, the proper datasource and connection will be obtained and used.
000000a1 JPAPUnitInfo E CWWJP0015E: An error occurred in the org.hibernate.jpa.HibernatePersistenceProvider persistence provider when it attempted to create the container entity manager factory for the App_DB persistence unit. The following error occurred: [PersistenceUnit: App_DB] Unable to build Hibernate SessionFactory
So it seems that Hibernate can't determine the DataSource
. When I set the full JDNI-name of the <jta-data-source>
instead of the reference like this
<jta-data-source>jdbc/app/appDB</jta-data-source>
the application starts perfectly and Hibernate can do schema validation and all on startup.
When I searched for this problem on the net I found the suggestion that you should add the javax.persistence.jtaDataSource"
to the persistence.xml
with the same value as the <jta-data-source>
like this:
<property name="javax.persistence.jtaDataSource" value="java:comp/env/jdbc/app/dataSourceRef" />
But this didn't changed anything in my case. Same error and no successfull startup.
So I'm interesting in getting answers to the follong questions:
Main question: How do I define my persistence.xml
to use the DataSource
reference again? It doesn't help if the start up validaten is made against the same database for all instances of the application, even if the running application then would use another datasource after startup, because I obvious want to validate the DataSource
the instance really uses. Note: Chaning the JPA implementation won't be done by our admins - they only stick to the original configuration cause of IBM support.
Minor question: Why does Hibernate (4.2.x) in combination with openJPA can use the reference in WAS 8.5, but Hibernate (5.2.18) in combination with eclipseLink can't do this in WAS 9.0?
Go give an update on this. My post is not a answer with a working solution but with further explainations which happens (as far as I understand) and why it fails. And to say it before it's a shortend version of my analysis I wrote for work (over 4 pages) as I didn't want to translate completly from German (my english is quite bad) and they are based on several log files I can't publish here.
td;dr
IBM has to change the WAS 9.
Long story
After another talk with our admins I did further research:
The error Unsupported use of GenericConnection
is thrown by the IBM and was even thrown on WAS 8.5 / Hibernate 4.2.x but the WAS/Hiberante combination worked another way as in 9.0/5.2.x
The error Unable to use JDBC Connection to create Statement
is thrown (in 5.2) by the IdTableHelper
when it tries to create a statement on a connection object (Statement statement = connection.createStatement();
), which was created by the EntityManagerFactory. I was wondering why the one error says it couldn't provide a connection? I came to the conclusion that the WAS gives back some kind of proxy item as otherwise, if the connection would be NULL the error message would be Unable obtain JDBC Connection
which would be triggered earlier in Hibernates code.
Further research:
Wenn an application is started (e.g. by clicking start
in the admin consol) the starting phase consists of two phases: In the first the container is start up, regestering EJB, JTA, JMS, JPA etc. and after that the application is started and all startup-beans (beans annotated with @StartUp
) are started. When there is an error, which the WAS considers as critical, the start up failes.
Hibernates tries to validate the schema (on hbm2dll=validate
) when the entitymanager was created.
When digging into further logs I found out that in Hibernate 4.2 the error came twice during setting up the JPA as there was no connection but when the startup-bean the logs show that the persistence.xml
is parsed again and the connection could be provided by the WAS. AS of this the schema validation was successful. During setting up the JPA the error message comes twice because retrieving the metadata and trying to validate are in two try/catch blocks in Hibernate 4.2.
When using WAS9/Hibernate 5.2 the error message is logged ones and the startup failes. The message is logged only once because retrieveing the metadata and doing the validation are in the same try/catch block and this failes already on recieveing the metadata. This error still doesn't break the start up of the application. But in difference to WAS 8.5/4.2 when the startup-bean is started the starting process failes as no entitymanager can be provided to the bean. Also the logs show that the persistence.xml
is not parsed again. So the WAS is not trying to provide an entitymanager a second time.
So without a "fix" or let's call it change of the WAS 9 the usage of datasource-refrence is not possible. And I doubt this will ever happen...
On more thing:
The mentioned IdTableHelper
class is the basic lass for Hibernates Bulk strategy and the two methods the error message can be thrown try to execute create or delete statements. As the database user of the application has no DDL rights I was thinking if this would be a problem and if I could change the behavior by changing the bulk strategy of hibernate to org.hibernate.hql.spi.id.inline.InlineIdsInClauseBulkIdStrategy
where no tables are created for cascading operations. But this didn't change anything as the metadata for the schema validation is still needed.