I have a Spring MvC project using JPA and Oracle as DB, with this entity:
#Entity
#Table(name = "AUTORISATION_TAURU")
#NoArgsConstructor
#AllArgsConstructor
#Data
#Builder
#EqualsAndHashCode(of = {"autorisationTaurusId"})
public class AutorisationTauru implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_TAURU")
#SequenceGenerator(sequenceName = "SEQ_AUTORISATION_TAURUS", allocationSize = 1, name = "SEQ_TAURU")
#Column(name = "AUTORISATION_TAURUS_ID")
private Long autorisationTaurusId;
..
}
in my xml config file, I have this;
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:~/test2;DB_CLOSE_DELAY=-1;MODE=Oracle;INIT=RUNSCRIPT FROM 'classpath:create_db.sql'\;
RUNSCRIPT FROM 'classpath:create_db2.sql'\;
RUNSCRIPT FROM 'classpath:create_func.sql'" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<bean id="jpaVendorAdapter"
class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="database" value="H2" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="bonanza-entities" />
<property name="packagesToScan">
<array>
<value>com.bonanza.model</value>
</array>
</property>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="jpaProperties">
<props>
<prop key="eclipselink.target-database">org.eclipse.persistence.platform.database.OraclePlatform</prop>
</props>
</property>
</bean>
I have created the table I am doing the INSERT using the AUTO_INCREMENT option:
CREATE TABLE IF NOT EXISTS AUTORISATION_TAURU
(
AUTORISATION_TAURUS_ID NUMBER ,
but when I run my local tests, I got this error:
Local Exception Stack:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLSyntaxErrorException:
Syntax error in SQL statement "SELECT SEQ_AUTORISATION_TAURUS.NEXTVAL FROM[*] DUAL"; expected "identifier"; SQL statement:
SELECT SEQ_AUTORISATION_TAURUS.NEXTVAL FROM DUAL [42001-200]
Error Code: 42001
Call: SELECT SEQ_AUTORISATION_TAURUS.NEXTVAL FROM DUAL
Query: ValueReadQuery(sql="SELECT SEQ_AUTORISATION_TAURUS.NEXTVAL FROM DUAL")
If I add the sequel creation:
CREATE SEQUENCE SEQ_AUTORISATION_TAURUS
MINVALUE 1 MAXVALUE 9223372036854775807
START WITH 1 INCREMENT BY 1 CACHE 8 NOCYCLE;
I got this error when running the test:
... 43 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Deployment of PersistenceUnit [bonanza-entities] failed. Close all factories for this PersistenceUnit.
Internal Exception: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLSyntaxErrorException: Sequence "SEQ_AUTORISATION_TAURUS" already exists; SQL statement:
Probably the problem with your setup consists in that your tests are creating several times the Spring application context.
Every time a new test is run it will recreate the dataSource bean and, in addition, it will try to launch the H2 database initialization scripts.
In a normal case, the first test will create the H2 database folder and related stuff, the next will reuse it.
Depending of the content of those scripts it will work most of the times, but not always as in your case.
To avoid that problem you have several options.
On one hand, in this specific case, you can include the clause IF NOT EXISTS in your sequence creation code:
CREATE SEQUENCE IF NOT EXISTS SEQ_AUTORISATION_TAURUS...
In a general case, you can modify your script to take into account this fact and create if not exists the different H2 elements or first DROP and then CREATE every one you need.
On the other hand, Spring Test also offers you the #DirtiesContext annotation for a similar purpose:
Test annotation which indicates that the ApplicationContext associated with a test is dirty and should therefore be closed and removed from the context cache.
And:
#DirtiesContext may be used as a class-level and method-level annotation within the same class or class hierarchy. In such scenarios, the ApplicationContext will be marked as dirty before or after any such annotated method as well as before or after the current test class, depending on the configured methodMode() and classMode().
As you can see, you only need to annotate your class or test methods with this annotation and Spring will recreate the context accordingly:
#DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
Please, be aware that this approach will have impact on the performance of your tests because the Spring application context need to be recreated although, on the other hand, it will provide you always a clean and deterministic - according to your initialization scripts - database state.
Related
I have a Java EE application which uses Hibernate 4.2.7 as persistence provider executing Junit tests in an embeddable Websphere 8.0.0 container. Database access works fine in a real (i.e. non-embedded) Websphere 8.0.0 instance. The unit tests do work when run with OpenJPA instead of Hibernate. However, running the Junit tests with Hibernate, I get the following exception:
CNTR0020E: EJB threw an unexpected (non-declared) exception during invocation of method "getEntity" on bean "BeanId(embeddable#classes#SomeBean, null)". Exception data: org.hibernate.service.jndi.JndiException: Unable to lookup JNDI name [java:comp/websphere/ExtendedJTATransaction]
at org.hibernate.service.jndi.internal.JndiServiceImpl.locate(JndiServiceImpl.java:68)
at org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform$TransactionManagerAdapter$TransactionAdapter.(WebSphereExtendedJtaPlatform.java:156)
at org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform$TransactionManagerAdapter$TransactionAdapter.(WebSphereExtendedJtaPlatform.java:152)
at org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform$TransactionManagerAdapter.getTransaction(WebSphereExtendedJtaPlatform.java:124)
at org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform$TransactionManagerAdapter.getStatus(WebSphereExtendedJtaPlatform.java:119)
at org.hibernate.engine.transaction.internal.jta.JtaStatusHelper.getStatus(JtaStatusHelper.java:73)
at org.hibernate.engine.transaction.internal.jta.JtaStatusHelper.isActive(JtaStatusHelper.java:115)
at org.hibernate.service.jta.platform.internal.TransactionManagerBasedSynchronizationStrategy.canRegisterSynchronization(TransactionManagerBasedSynchronizationStrategy.java:56)
... stripped ...
It seems the implementation of WebsphereExtendedJtaPlatform is trying to get the current transaction via a JNDI lookup but fails because that JNDI name does not exist in the embedded container. Here's a snipped from org.hibernate.service.jta.platform.internal.WebsphereExtendedJtaPlatform:
public class TransactionAdapter implements Transaction {
private TransactionAdapter() {
if ( extendedJTATransaction == null ) {
extendedJTATransaction = jndiService().locate( "java:comp/websphere/ExtendedJTATransaction" );
}
}
... stripped ...
The class ExtendedJtaTransaction itself does exist on the class path inside com.ibm.ws.runtime.jar.
The settings in our persistence.xml look like this:
<persistence-unit name="BLA" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/BLA</jta-data-source>
<class>com.some.Entity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="hibernate.archive.autodetection" value="class" />
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.WebSphereExtendedJTATransactionLookup" />
<property name="jta.UserTransaction" value="java:comp/UserTransaction" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.jdbc.fetch_size" value="100" />
<property name="hibernate.temp.use_jdbc_metadata_defaults" value="false" />
</properties>
Does anyone have a solution for this?
Thanks!
Transaction strategy configuration
Hibernate requires the configuration of two essential pieces in order to properly run with transactions. The first, hibernate.transaction.factory_class, defines transactional control and the second, hibernate.transaction.manager_lookup_class, defines the mechanism for registration of transaction synchronization so the persistence manager is notified at transaction end when it needs to synchronize changes with the database. For transactional control, both container-managed and bean-managed configurations are supported. The following properties must be set in Hibernate.cfg.xml when using Hibernate with WebSphere Application Server:
for container-managed transactions:
<property name="hibernate.transaction.factory_class">
org.hibernate.transaction.CMTTransactionFactory
</property>
<property name="hibernate.transaction.manager_lookup_class">
org.hibernate.transaction.WebSphereExtendedJTATransactionLookup
</property>
for bean-managed transactions:
<property name="hibernate.transaction.factory_class">
org.hibernate.transaction.JTATransactionFactory
</property>
<property name="hibernate.transaction.manager_lookup_class">
org.hibernate.transaction.WebSphereExtendedJTATransactionLookup
</property>
<property name="jta.UserTransaction">
java:comp/UserTransaction
</property >
The jta.UserTransaction property configures the factory class to obtain an instance of a UserTransaction object instance from the WebSphere container.
The hibernate.transaction.manager_lookup_class property is supported on the WebSphere platform by WebSphere Application Server V6.x and later, and on WebSphere Business Integration Server Foundation V5.1 and later. This property configures Hibernate to use the ExtendedJTATransaction interface, which was introduced in WebSphere Business Integration Server Foundation V5.1 and WebSphere Application Server V6.0. The WebSphere ExtendedJTATransaction interface establishes a pattern that is formalized in Java EE 5 via the JTA 1.1 specification.
There are many variations on the setup of multiple persistence contexts within spring, but none that explain the problem that I am facing.
I have an application context structure like this:
core AC
`-- web-root AC
In my core application context i have defined both my two datasources and a transactionManager as well as a single EntityManagerFactory bean:
<!-- core applicationContext.xml -->
<beans ...>
<context:annotation-config/>
...
<!-- Container managed datasources -->
<jee:jndi-lookup id="module1DS" jndi-name="jdbc/module1DS" expected-type="javax.sql.DataSource"/>
<jee:jndi-lookup id="securityDS" jndi-name="jdbc/securityDS" expected-type="javax.sql.DataSource"/>
<bean id="transactionManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager"/>
<bean id="securityEMF" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="securityPU"/>
<property name="jtaDataSource" ref="securityDS"/>
<property name="packagesToScan" value="com.example.security.model"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter"/>
</property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.websphere.WebSphereLoadTimeWeaver"/>
</property>
</bean>
</beans>
In the web-root application context i have defined an additional EMF:
<!-- module1 applicationContext.xml -->
<beans...>
....
<bean id="module1EMF" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="module1PU"/>
<property name="jtaDataSource" ref="module1DS"/>
<property name="packagesToScan" value="com.example.module1.model"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter"/>
</property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.websphere.WebSphereLoadTimeWeaver"/>
</property>
</bean>
</beans>
Now I have a couple entities defined under the com.example.core.model package in the core module and also a single entity defined in the com.example.module1.model package in module1 (which is a war in an ear).
There is a service defined in core that gets the EntityManager injected into with the securityPU PersistenceContext and performs a named query on an entity in the database:
#Service("coreThingService")
public class CoreEntityThingServiceImpl implements CoreEntityThingService {
#PersistenceContext(unitName = "securityPU")
#Qualifier("securityEMF")
private EntityManager em;
#Override
public List<CoreEntityThing> getCoreEntityThingById(String id) {
// select a from CoreEntityThing a where a.id = :id
TypedQuery<CoreEntityThing> query = em.createNamedQuery("CoreEntityThing.findById", CoreEntityThing.class);
query.setParameter("id", id);
return query.getResultList();
}
}
nevermind about my unusual logic here, the point is that when this service is injected into the web-tier inside a #Controller and invoked, I get the following error:
org.apache.openjpa.persistence.ArgumentException: An error occurred while parsing the
>query filter "select a from CoreEntityThing a where a.id = :id". Error message: The
>name "CoreEntityThing" is not a recognized entity or identifier. Perhaps you meant
>Module1Entity, which is a close match. Known entity names: [Module1Entity]
I have Inspected the injected em:
#PersistenceContext(unitName = "securityPU")
#Qualifier("securityEMF")
private EntityManager em;
and have found it to be the securityPU as expected, what i didn't expect however is when it said that it couldn't find the entities (even though the packagesToScan property of the securityEMF was correctly pointing to the right package).
More strangely, it suggested an entity in the 'child' PU -- Module1Entity -- that should not have been picked up in the securityEMF.
What am i doing wrong?
UPDATE 1: I have updated my application context xml descriptions to make clear how i have created my PUs
UPDATE 2: It appears my classes are being scanned as I can see them in the persistence unit at runtime, what is really intriguing, is that up until the invokation of the createNamedQuery method on the EntityManager proxy is called (using method.invoke(targetPU, name) where method is the createNamedQuery function and targetPU is the securityEMF PU), The PU remains as the securityEMF. I can't go any deeper into code to see what createNamedQuery is actually doing, but during this invokation, the described error is thrown! what on earth is happening?!
To make things clearer, The error message's query string:
"select a from CoreEntityThing a where a.id = :id"
sits on top of my CoreEntityThing class!
package com.example.core.model.security;
...
#Entity
#NamedQuery(name = "CoreEntityThing.findById", query = "select ...")
public class CoreEntityThing { ... }
so it is finding the entity, then claiming it knows nothing about it!
UPDATE 3: I've read in this blog post about a supposed limitation of JPA PersistenceUnits having limited modularity. can anyone point me to more documentation about this limitation and any examples of multi module enterprise solutions using multiple datasources. This has to be a common concern for a large number of sizable projects.
I'm using java 1.6 and spring 3.0.4, I want to realize a java functionality that
calculate new data values
update one-by-one the existing values on the database
If in any of this step there's an error I want to rollback the whole transaction and come back to the previous state.
I already realized all this pieces of code, I just want to put them together. How I can manage this with the existing spring values that are working with #Entity and #Column annotations?
Thanks!
Short answer: as you're using Spring, the easiest would be to use the transaction management, creating a service that represents this transaction unit and annotate the method with #Transactional
In practice, you need to configure a PlatformTransactionManager in your application. As you seem to use JPA, the JpaTransationManager seems like an obvious choice. To enable the processing of the #Transactional annotation, you can either use #EnableTransactionManagement or the <tx:annotation-driven/> namespace. Both are explained in the Javadoc of #EnableTransactionManagement
By default, a runtime exception thrown from that annotated method will manage a transaction rollback. If your code is using checked exceptions, you'll have to configure the rollbackFor attribute of the annotation.
There are more details and examples available in the documentation
For people that need the same configuration, here you can find how I solved this problem, integrating Hibernate with Spring.
<!-- session factory activate the transaction modules for the specified classes -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="configLocation" value="classpath:config-hibernate.xml" />
<property name="packagesToScan">
<list>
<!-- Additional packages required to be added if entities located elsewhere -->
<value>com.some.package.dao</value>
<value>com.some.package.model</value>
<value>com.some.package.SpecificClass</value>
</list>
</property>
<property name="mappingResources" ref="mappingResources"/>
<bean id="mappingResources" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<!-- here you can add your hibernate mapping query that you want to use on transaction -->
<value>config-hibernate-mapping.xml</value>
</list>
</property>
</bean>
<!-- This will activate transactional annotation -->
<tx:annotation-driven transaction-manager="transactionManager" />
#Service
#Transactional
public class SpecificClass {
// write your method, everyone of them will be transactional
// and there will be a commit in case of success or rollback in case of exception
}
Update: It seems it's failing after one insert, #412, fails a not null constraint at the database level. The transaction is probably rolling itself back. Given this setup, is it possible to get a new transaction established?
I'm trying to insert a lot of rows into my oracle database, and JPA works just fine until about the 400th insert. I expect to have several thousand rows to insert.
Here's my psuedo-code (shortened for clarity) & persistence.xml:
#Stateless
public class LocalContentService
{
#Inject EntityManager em;
public void mySavingMethod(){
for(Foo foo : fooDao.fetchAllFoos()){
Bar bar = new Bar(foo);
em.persist(bar);
em.flush();
em.clear();
log.debug("Saved content for: " + bar.getId());
}
}
<persistence-unit name="databaseTest" >
<jta-data-source>java:/jdbc/testDS</jta-data-source>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<class>org.myorg.Bar</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="false" />
<property name="hibernate.use_sql_comments" value="true" />
</properties>
</persistence-unit>
After roughly 400 rows, I get this error message and all subsequent inserts fail:
ERROR [stderr] (http--127.0.0.1-8080-1)
javax.persistence.TransactionRequiredException: JBAS011469:
Transaction is required to perform this operation (either use a
transaction or extended persistence context)
So my question is two fold
1) What on earth happened to my transaction midway through the process? Can it be avoided?
2) Is there a better approach to doing a bulk insert like this one (keeping in mind that i'm loading a bunch of Foo's and they need to be transformed into Bar's before persisting.
I'm running inside a jBoss 7.1.1.Final AS and hibernate-jpa-2.0-api
Suppose you use JPA with Spring, with Hibernate as JPA implementation.
JPA transaction mode is "JTA", so you need to pass the container transactionManager to Hibernate.
The classical answer is to set hibernate.transaction.manager_lookup_class to the matching class for your server.
However, I think it's a shame to have this depend of server specific configuration as you already found the transactionManager in Spring with <tx:jta-transaction-manager>.
Is there a way to give this transactionManager to Hibernate with a configuration like
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="persistence_unit_name"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.transaction.manager_lookup_class">
org.hibernate.transaction.SunONETransactionManagerLookup
</prop>
</props>
</property>
</bean>
<tx:jta-transaction-manager/>
The goal is to get rid of the org.hibernate.transaction.SunONETransactionManagerLookup property.
By the way, I really have two different server implementations in mind.
EDIT : without the transaction manager configuration, Hibernate chokes when creating the EntityManagerFactory :
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in URL [file:/C:/configuration/afoCuad-metier-ear/entitymanager-base-context.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: fr.tm.ima.cuad-afoCuad-metier-ejb-PU] Unable to build EntityManagerFactory
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1420)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:529)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:495)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:656)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:629)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:147)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:338)
... 80 more
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: fr.tm.ima.cuad-afoCuad-metier-ejb-PU] Unable to build EntityManagerFactory
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:901)
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:225)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:308)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
... 93 more
Caused by: org.hibernate.HibernateException: The chosen transaction strategy requires access to the JTA TransactionManager
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:401)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1385)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:954)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:892)
... 98 more
First of all - do you really need JTA? Typically spring+hibernate don't require it. You can use a simple JpaTransactionManager / HibernateTransactionManager.
If you really want JTA, then you will need a JTA provider. If not running in an application server, check this question for how to use JTA in a servlet container. (Also take a look at this question)
Finally, hibernate docs specify that, for container-managed transactions:
Declarative transaction demarcation is a standard feature of EJB, also known as container-managed transactions (CMT). In EJB 2.x you would use XML deployment descriptors to create your transaction assembly. In EJB 3.x you can use JDK 5.0 annotation metadata directly in your source code, a much less verbose approach. To enable CMT transaction demarcation for EJBs in Hibernate configuration:
set hibernate.transaction.manager_lookup_class to a lookup strategy for your JEE container
set hibernate.transaction.factory_class to org.hibernate.transaction.CMTTransactionFactory
The second point is perhaps something you've missed?
What the documentation says in addition to that (the next section) is that if you want declarative transaction, hibernate is not where you should look at. You'd need to create an interceptor. That's exactly what spring transaction managers are. And that would be my choice given your technology stack (Spring).
If you wish not to rely on a single JTA provider, then make two builds. For example maven has "maven profiles", which allow to make builds for different environments.
Unfortunately if one looks at the Hibernate APIs like many other JBoss products they have a a class typically called Configuration to hold most if not all the main config stuff. Unfortunately they (JBoss) seem to like to hold "Strings" for parameters and class to locate instances. Almost always its often impossible to simply set an actual premade ready to go setup.
I am about to try something similar to the following for the very same reason you are mentioning.
Create an implementation of TransactionManagerLookup
include a setter which takes a TM and sets a thread local variable + instance.
pass the name of TML inside the properties you pass to the Configuration.
When Your TML startsup copy the thread local variable to your instance fie.d.
clear the threadlocal once everything is done.
I've recently been doing some stuff with JPA/Grails and the configuration I used was along these lines:
Does this help at all?
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource"/>
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class="org.hibernate.ejb.EntityManagerFactoryImpl">
<constructor-arg index="0" ref="sessionFactory"/>
<constructor-arg index="1">
<bean id="javax.persistence.spi.PersistenceUnitTransactionType.RESOURCE_LOCAL" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
</constructor-arg>
<constructor-arg index="2" value="true"/>
<constructor-arg index="3"><null/></constructor-arg>
</bean>