Use JPA in Java SE application with support for transactions - java

I am trying to develop a small program that uses JPA, but can run without an application server, i.e. as a regular Java SE application.
AFAIK, for JavaEE the application server takes care of starting and ending database transactions. Now my problem is that I get this error from some internal code using JPA:
Exception in thread "main" javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1171)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1332)
at <see below>
The code causing this exception is equivalent to this:
String persistenceUnitName; // actually comes from some config file
EntityManager entityManager = Persistence.createEntityManagerFactory(persistenceUnitName).createEntityManager();
entityManager.flush(); // exception is thrown here
I cannot change that code, so it relies on a transaction being automagically started.
Wrapping the call to the above code in a custom transaction like this:
String persistenceUnitName; // same persistence unit name
EntityManager entityManager = Persistence.createEntityManagerFactory(persistenceUnitName).createEntityManager();
entityManager.getTransaction().begin();
callAboveCode();
entityManager.getTransaction().commit();
does not help. the call to the factory's createEntityManager() returns a new entity manager each time, resulting in a different persistence context I suppose.
Is there a way to get automatic transactioning, similar to running JPA in an application server, in a Java SE application?

In SE you have to be careful only using one instance of EntityManager in the whole application. Use a singleton ore something similar for managing that instance.
You also have to manage transaction "manually" - like:
em.getTransaction().begin();
em.persist(anyobject);
em.getTransaction().commit();

Related

Java EntityManager transaction gets broken, how to locate the SQL that causes it?

I have an EntityManager and a JobDAO class which has many methods which use the EntityManager to select/update/delete/insert.
For some methods, I now am getting a javax.persistence.TransactionRequiredException: Executing an update/delete query ..
about not having a transaction.
I have a #Transactional annotation on the method calling the other methods.
I have now fixed it somewhat by using my own database connection for some of these commands, but I'd like to find the SQL that causes the problem.
One idea I have is to add a transaction checker method call to the end of each of the 20 methods that are suspicious. But I'd like to know whether you have a better idea to check on the EntityManager, for example by logging all SQL so I can find the last SQL where it stopped working.
See this post on SO for configuring the ORM provider (e.g. Hibernate, etc.) to log sql:
How to view the SQL queries issued by JPA?
In addition, you can programatically ask the EntityManager if it is currently in a transaction, see isJoinedToTransaction().
Also..., make sure that the class with the methods that have the #Transactional annotation is a Spring Bean, otherwise the annotation does nothing.
In addition you have to tell Spring to enable transaction management. This post will help you understand how to do that: https://www.baeldung.com/transaction-configuration-with-jpa-and-spring

How JPA with type is JTA handled by EJB container

I used jboss, and hibernate 4 within jpa (JTA transaction type). In a DAOUtil class, I try to get entity manager, I can get it and do a query. But when I flush entity manager, the error happened.
Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:970)
at com.vms.dao.hibernate.HibernateDAOSession.flush(HibernateDAOSession.java:288)
I searched on internet, and they say: if we use jpa with jta transaction type, we must inject EntityManager into a ejb, is this correct?
If this is correct, I've a concern: how about threadpool for entity manager? I confused alot of thing about that.
Please anyone give my the true way, to use jpa within JTA transaction type.
Added.
I think this issue because I've a getter to get EntityManager outside EJB.
One more question, in JPA with JTA transaction type, I think we could not update by native sql. IN ejb I've below code.
public int executeNativeUpdate(String query, Map<String, Object> params) {
Query q = em.createNativeQuery(query);
setParameters(q, params);
return q.executeUpdate();
}
Error raised when running into code:
Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query
Anyone can explain. I'm beginer in JPA adn JTA. If I use JPA with JTA transaction type and inject the EntityManager into Stateless EJB, EJB container will handle transaction for me, right? I no need do begin/commit a transaction. How about native sql? Can EJB container handle all?
If you use JTA transactional type then container manage transactions (application server, or openjpa, or other). In this case you should use EJB, where every method call is wraped in transaction. Create, commit, rollback, manage entity manager - work of container.

Why can I insert/update data with hibernate even when there's no transaction involved?

As we all know that in Hibernate if no transaction commit, the changes won't affect in database. But I found something weird. And the code as follows:
ApplicationContext ctx = new ClassPathXmlApplicationContext("Spring.xml");
SessionFactory sessionFactory = (SessionFactory) ctx.getBean("sessionFactory");
Session session = sessionFactory.openSession();
Model model = new Model();
...
session.save(model);
session.flush();
session.close();
And the model was saved to database even there's no transaction, anyone can explain this?
Any comments would be appreciated! Thanks!
PS: I am using mysql.
The session.flush command saved the transaction. If it's wrong, you should use transaction.
usually hibernate needs the line session.beginTransaction(); to work. You didn't write that and your application worked, I guess your application runs in an Application server, which provides transaction management. e.g. jboss, weblogic...
However it doesn't mean that there is no transaction. Did you set auto-commit true?
btw, session.flush() and txn.commit() are different.
Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.
After session.flush(), you still can call txn.rollback() to rollback all changes.
edit
oh I saw you used spring. did you configured txnmanager in spring?
Hibernate doesn't need transactions, the most common problems in database-based applications are just easier to solve with transactions which is why usually everyone uses transactions with Hibernate. But that's mere coincidence/convention/laziness.
All Hibernate needs is a java.sql.Connection and if your container provides one even though there is no current transaction manager configured, Hibernate is fine with that.
In fact, Hibernate has no idea that there might be a transaction manager. So session.flush() will use the ApplicationContext to get a connection, generate the SQL and use JDBC to send the generated SQL code to the database.
From Hibernate's point of view, that's all that happens.
There can be several reasons why the data is committed to the database:
You forgot to turn of auto commit on the connection.
Your web container / spring config automatically wires a transaction manager that synchronizes with HTTP requests.
Your code is called form another method which is annotated with #Transactional; in this case, you inherit the existing transaction.

"suspend" hibernate session managed by spring transaction manager

Is there any way to remove/suspend a current spring managed hibernate session from a thread so a new one can be used, to then place the original session back onto the thread? Both are working on the same datasource.
To describe the problem in more detail. I'm trying to create a plugin for a tool who has it's own spring hibernate transaction management. In this plugin I would like to do some of my own database stuff which is done on our own spring transaction manager. When I currently try to perform the database actions our transaction manager starts complaining about an incompatibly transactionmanager already being used
org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.
A workaround that seems to do the trick is running my own code in a different thread and waiting for it to complete before I continue with the rest of the code.
Is there a better way then that, seems a bit stupid/overkill? Some way to suspend the current hibernate session, then open a new one and afterworths restoring the original session.
Is there any reason you can't have the current transaction manager injected into your plugin code? Two tx managers sounds like too many cooks in the kitchen. If you have it injected, then you should be able to require a new session before doing your work using the #transactional annotation's propagation REQUIRES_NEW attribute see the documentation for an example set-up
e.g.
#transactional(propogation = Propogation.REQUIRES_NEW)
public void addXXX(Some class) {
...
}
But this would use spring's PlatformTransactionManager rather than leaving it up to hibernate to manage the session / transaction.

HibernateDaoSupport , transaction is not rolled back

I'm playing around with Spring + Hibernate and some "manual" transaction management with PostgreSQL
I'd like to try this out and understand how this works before moving to aop based transaction management.
#Repository
public class UserDAOImpl extends HibernateDaoSupport implements UserDAO {
#Override
public void saveUser(User u) {
Transaction tx = getSession().beginTransaction();
getHibernateTemplate().saveOrUpdate(u);
tx.rollback();
}
}
Calling saveUser here, I'd assume that saving a new User will be rolled back.
However, moving to a psql command line, the user is saved in the table.
Why isn't this rolled back, What do I have to configure to do transactions this way ?
Edit; a bit more debugging seems to indicate getHibernateTemplate() uses a different session than what getSession() returns (?)
Changing the code to
Transaction tx = getSession().beginTransaction();
getSession().persist(u);
tx.rollback();
and the transaction does get rolled back. But I still don't get why the hibernateTemplate would use/create a new session..
A couple of possibilities spring to mind (no pun intended):
a) Your JDBC driver defaults to autocommit=true and is somehow ignoring the beginTransaction() and rollback() calls;
b) If you're using Spring 3, I believe that SessionFactory.getSession() returns the Hibernate Session object wrapped by a Spring proxy. The Spring proxy is set up on the Session in part to handle transaction management, and maybe it's possible that it is interfering with your manual transaction calls?
While you can certainly use AOP-scoped proxies for transaction management, why not use the #Transactional(readOnly=false|true) annotation on your service layer methods? In your Spring config file for your service layer methods, all you need to do to make this work is to add
<tx:annotation-driven />
See chapters 10 and 13 of the Spring Reference Documentation on Transaction Management and ORM Data Access, respectively:
http://static.springsource.org/spring/docs/3.0.x/reference/index.html
Finally, if you're using Spring 3, you can eliminate references to the Spring Framework in your code by injecting the Spring-proxied SessionFactory bean into your DAO code - no more need to use HibernateDaoSupport. Just inject the SessionFactory, get the current Session, and use Hibernate according to the Hibernate examples. (You can combine both HibernateDaoSupport and plain SessionFactory-based Hibernate code in the same application, if required.)
If you see the JavaDoc for HibernateDaoSupport.getSession() it says it will obtain a new session or give you the one that is used by the existing transaction. In your case there isn't a transaction listed with HibernateDaoSupport already.
So if you use getHibernateTemplate().getSession() instead of just getSession(), you should get the session that is used by HibernateTemplate and then the above should work.
Please let me know how it goes.
EDIT:
I agree its protected...my bad. So the other option then is to keep the session thread bound which is usually the best practice in a web application. If HibernateDaoSupport is going to find a thread bound session then it will not create a new one and use the same one. That should let you do rollbacks.

Categories

Resources