Hibernate nativeQuery - transaction?

We have a service that @Statefull . Most data operations are atomic, but within a certain set of functions. We want to run multiple native queries within the same transaction.

We introduced the EntityManager transaction persistence context. When creating a "bunch" of ordinary objects using em.persist() , everything works fine.

But when using its own queries (some tables are not represented by any @Entity ), Hibernate does not start them in a single transaction, but basically uses ONE transaction for each query.

So, I already tried to use manual records START TRANSACTION; and COMMIT; , but it seems to intervene in transactions, hibernate uses to save entities when mixing its own queries and calls of constant duration.

 @Statefull class Service{ @PersistenceContext(unitName = "service") private EntityManager em; public void doSth(){ this.em.createNativeQuery("blabla").executeUpdate(); this.em.persist(SomeEntity); this.em.createNativeQuery("blablubb").executeUpdate(); } } 

Everything inside this method should happen within a single transaction. Is this possible with Hibernate? When debugging, it is clearly seen that each statement occurs “independently” from any transaction. (Ie Changes are dumped to the database immediately after each statement.)


I checked the example below with minimal setup to rule out any other problem factors (the rows are only for checkpoints to view the database after each query):

 @Stateful @TransactionManagement(value=TransactionManagementType.CONTAINER) @TransactionAttribute(value=TransactionAttributeType.REQUIRED) public class TestService { @PersistenceContext(name = "test") private EntityManager em; public void transactionalCreation(){ em.createNativeQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','c')").executeUpdate(); String x = "test"; em.createNativeQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','c','b')").executeUpdate(); String y = "test2"; em.createNativeQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('c','b','a')").executeUpdate(); } } 

Sleep mode is configured as follows:

 <persistence-unit name="test"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <jta-data-source>java:jboss/datasources/test</jta-data-source> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" /> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" /> <property name="hibernate.archive.autodetection" value="true" /> <property name="hibernate.jdbc.batch_size" value="20" /> <property name="connection.autocommit" value="false"/> </properties> </persistence-unit> 

And the result is the same as in autocommit mode: after each native query, the database (viewing contents from the second connection) is immediately updated.


The idea of ​​using a transaction using manuall leads to the same result:

 public void transactionalCreation(){ Session s = em.unwrap(Session.class); Session s2 = s.getSessionFactory().openSession(); s2.setFlushMode(FlushMode.MANUAL); s2.getTransaction().begin(); s2.createSQLQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','c')").executeUpdate(); String x = "test"; s2.createSQLQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','c','b')").executeUpdate(); String y = "test2"; s2.createSQLQuery("INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('c','b','a')").executeUpdate(); s2.getTransaction().commit(); s2.close(); } 
+8
java mysql hibernate jpa transactions
source share
3 answers

If you are not using container-managed transactions, you also need to add a transaction policy:

@Stateful @TransactionManagement (value = TransactionManagementType.CONTAINER) @TransactionAttribute (value = MANDATORY)

I saw this phenomenon only in two situations:

  • The DataSource operates in automatic commit mode, so each statement is executed in a separate transaction.
  • EntityManager was not configured using @Transactional, but then only requests can be executed, since any DML operation would complete the return of the required exception transaction.

Let recap you set the following Hibernate properties:

 hibernate.current_session_context_class=JTA transaction.factory_class=org.hibernate.transaction.JTATransactionFactory jta.UserTransaction=java:comp/UserTransaction 

If the final property should be set using your ApplicationTransaction JNDI naming name.

You can also use:

 hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JBossTransactionManagerLookup 

or another strategy according to your current application server.

+3
source share

After reading the topic for another heap of hours while playing with each configuration property and / or annotation, I could find a working solution for my utility. This may not be the best or the only solution, but since the question received several bookmarks and upvotes, I would like to share what I still have:

Firstly, there was no way to make it work as expected when starting the save unit in managed mode. ( <persistence-unit name="test" transaction-type="JTA"> is the default JTA if no value is specified.)

I decided to add another constant constant to the xml constant, which is configured to work in unmanaged mode: <persistence-unit name="test2" transaction-type="RESOURCE_LOCAL"> .

(Note: the distortion of several units with conservation is simply a reason the eclipse of which cannot cope. It has no functional effect at all)

The unmanaged persitence-context requires a local database configuration because it is no longer provided by the container:

 <persistence-unit name="test2" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <class>test.AEntity</class> <properties> <property name="hibernate.connection.url" value="jdbc:mysql://localhost/test"/> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" /> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.password" value="1234"/> <property name="hibernate.connection.username" value="root"/> <property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.archive.autodetection" value="true" /> <property name="hibernate.jdbc.batch_size" value="20" /> <property name="hibernate.connection.autocommit" value="false" /> </properties> </persistence-unit> 

Now the change the project needs will add unitName when you use the @PersistenceContext annotation to get a managed EntityManager instance.

But keep in mind that you can use @PersistenceContext only for a managed persistence unit. For unmanaged, you can implement a simple Producer and Inject EntityManager using CDI if necessary:

 @ApplicationScoped public class Resources { private static EntityManagerFactory emf; static { emf = Persistence.createEntityManagerFactory("test2"); } @Produces public static EntityManager createEm(){ return emf.createEntityManager(); } } 

Now, in the example shown in the source column, you need to enter the EntityManager and manually take care of the transactions.

 @Stateful public class TestService { @Inject private EntityManager em; public void transactionalCreation() throws Exception { em.getTransaction().begin(); try { em.createNativeQuery( "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','a')") .executeUpdate(); em.createNativeQuery( "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','b')") .executeUpdate(); em.createNativeQuery( "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','c')") .executeUpdate(); em.createNativeQuery( "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','d')") .executeUpdate(); AEntity a = new AEntity(); a.setName("TestEntity1"); em.persist(a); // force unique key violation, rollback should appear. // em.createNativeQuery( // "INSERT INTO `ttest` (`name`,`state`,`constraintCol`)VALUES('a','b','d')") // .executeUpdate(); em.getTransaction().commit(); } catch (Exception e) { em.getTransaction().rollback(); } } } 

My tests have so far shown that mixing your own requests and challenges of perseverance leads to the desired result: either everything is completed, or the transaction is rollback as a whole.

At the moment, the solution is working. I will continue to test its functionality in the main project and check if there are other side effects.

Another thing I need to check is to save it:

  • Add both versions of EM to one Bean and use a combination. (The first checks seem to work, even while using both ems in the same table at the same time.)
  • The presence of both versions of EM running on the same data source. (The same data source will most likely not be a problem, the same tables that, I assume, can lead to unexpected problems.)

ps .: This is project 1. I will continue to improve the answer and point out the problems and / or weaknesses that I am going to find.

0
source share

You need to add <hibernate.connection.release_mode key="hibernate.connection.release_mode" value="after_transaction" /> to your properties. After a reboot, working with a transaction works.

0
source share

All Articles