My EntityManager is persisting/committing data to a Postgres database no problem. However, the connections it makes get stuck at 'Idle in transaction'. Here's my code:
public User create(User user) {
if(logger.isDebugEnabled()) {
logger.info("creating user: {}", user);
}
EntityManager entityManager = DbUtil.factory.createEntityManager();
try {
entityManager.getTransaction().begin();
// Persist takes an entity instance, adds it to the context and makes that instance managed (ie future updates
// to the entity will be tracked).
entityManager.persist(user);
entityManager.getTransaction().commit();
}
catch(RuntimeException e) {
throw getDbException(e);
}
finally {
entityManager.close();
}
return user;
}
Any idea why they aren't closing?
You say that persisting works, probably you see your data in db. But do you see all the data, weren't there failures? Your code doesn't handle transaction rollbacks, that might be the reason you see connection in 'Idle in transaction' state.
If an exception is thrown by throw getDbException(e), the transaction is not rolled back.
Related
I have a doubt, using getCurrentSession of a sessionFactory, it generates many connections to the database.
P6Spy logs
when it reaches about 400 the application crashes:
Crash
A typical method of a query by hibernate:
#Autowired
#Qualifier(value = "sessionFactory")
private SessionFactory sessionFactory;
try {
Session s = this.sessionFactory.getCurrentSession();
Query query = s.createQuery("from x where c.numFactura = :numFactura");
query.setParameter("numFactura", numFactura);
return query.uniqueResult();
} catch (Exception ex) {
throw ex;
}
I really don't know, if these events are connected, what do you think?
the problem wasn't there, I had a problem with the Jasper Reports DB Conection, fixed by autoclosing conection on timeout.
My problem is that JPA/Hibernate returns true for a call of entityManager.getTransaction().isActive() even when I did not explicitly start a transaction (see code below).
The problem here is that I want to read something from the database and a SerializationException is ok in this scenario, because this just indicates that the persisted object does not fit to the actual code any more and needs to be recalculated. Instead of just returning null the code below throws the following exception:
Transaction rollback failed.
org.hibernate.TransactionException: Unable to rollback against JDBC Connection
This shows me, there must have been a transaction somewhere which I did not start in my code. The finally block in the code above is
final EntityManager entityManager = Persistence.createEntityManagerFactory("test").createEntityManager();
try {
final TypedQuery<Test> query = entityManager.createQuery("SELECT t FROM test t", Test.class);
return query.getResultList();
} catch (final PersistenceException e) {
if (e.getCause() instanceof SerializationException) {
LOG.debug("Implementation changed meanwhile. That's ok - we return null.");
return null;
}
throw e;
} finally {
EntityManagerCloser.closeEntityManager(entityManager);
}
And the EntityManagerCloser looks like this:
public final class EntityManagerCloser {
private static final Logger LOG = LoggerFactory.getLogger(EntityManagerCloser.class);
public static void closeEntityManager(EntityManager entityManager) {
if (entityManager.getTransaction().isActive()) {
try {
entityManager.getTransaction().rollback();
} catch (PersistenceException | IllegalStateException e) {
LOG.error("Transaction rollback failed.", e);
}
}
if (entityManager.isOpen()) {
try {
entityManager.close();
} catch (IllegalStateException e) {
LOG.error("Closing entity manager failed.", e);
}
}
}
}
Hibernate docs says "Always use clear transaction boundaries, even for read-only operations". So do I really need to insert a
entityManager.getTransaction().begin();
....
<do read here>
....
entityManager.getTransaction().commit();
around every read operation I perform on the database?
I could implement another closeEntityManager method for read-only operations without the rollback transaction block but I want to understand why there IS a transaction at all. Thanks for any help!
The problem is that when you call entityManager.getTransaction(); a new transaction object will be created. So it is better to save the transaction reference to a variable as shown below.
Transaction txn = entityManager.getTransaction();
if (txn.isActive()) {
try {
txn.rollback();
} catch (PersistenceException | IllegalStateException e) {
LOG.error("Transaction rollback failed.", e);
}
}
Thanks to Jobin I quickly found the solution to my problem:
I think I need to call entityManager.isJoinedToTransaction() in my closeEntityManager method before calling entityManager.getTransaction().isActive().
This will prevent the EntityManagerCloser to start its own transaction which I can not rollback later because I did not explicitly call transaction.begin() on it.
In Java Hibernate, when we need to do something with DB we need:
1. Open session
2. Begin transaction
3. Finish transaction
4. Close session
For example if I want to get Student list:
public static List<Student> getStudentList()
{
List<Student> l = null;
Session session = HibernateUtil.getSessionFactory().openSession();
try {
String hql = "from Student";
Query query = session.createQuery(hql);
l = query.list();
} catch (HibernateException ex) {
//Log the exception
System.err.println(ex);
} finally {
session.close();
}
return l;
}
Insert a student
public static boolean addStudent(Student s)
{
Session session = HibernateUtil.getSessionFactory().openSession();
if (... /* check if student is already exists*/)
{
return false;
}
Transaction transaction = null;
try {
transaction = session.beginTransaction();
session.save(s);
transaction.commit();
} catch (HibernateException ex) {
//Log the exception
transaction.rollback();
System.err.println(ex);
} finally {
session.close();
}
return true;
}
Why there is no transaction in getStudentList()? Thank in advance
This hibernate article explains the behavior of SELECT operations which are not explicitly executed within a transaction.
The following example is given in the article but it's also true for your query example.
Session session = sessionFactory.openSession();
session.get(Item.class, 123l);
session.close();
A new Session is opened. It doesn’t obtain a database connection at this point.
The call to get() triggers an SQL SELECT. The Session now obtains a JDBC Connection from the connection pool. Hibernate, by default, immediately turns off the autocommit mode on this connection with setAutoCommit(false). This effectively starts a JDBC transaction!
The SELECT is executed inside this JDBC transaction. The Session is closed, and the connection is returned to the pool and released by Hibernate — Hibernate calls close() on the JDBC Connection. What happens to the uncommitted transaction?
The answer to that question is, “It depends!” The JDBC specification doesn’t say anything about pending transactions when close() is called on a connection. What happens depends on how the vendors implement the specification. With Oracle JDBC drivers, for example, the call to close() commits the transaction! Most other JDBC vendors take the same route and roll back any pending transaction when the JDBC Connection object is closed and the resource is returned to the pool.
Obviously, this won’t be a problem for the SELECT [...]
Lets extend the above example to provoke a possible problem.
Session session = sessionFactory.openSession();
Item item = session.get(Item.class, 123l);
item.setPrice(10);
session.close();
Now it depends on the JDBC driver if the new price for the Item is persisted or not.
So you can neglect begin and commit transactions on pure SELECT operations even when your JDBC driver will rollback the transaction as long as you have no database changes.
But anyway I would strongly recommend using transactions on any operation to avoid misunderstanding and problems.
My question is about hibernate, actually I'm working on a Java EE application using hibernate and mysq.
Everything looks fine. but I still have one problem when I insert data via phpMyAdmin to my database, I cannot access them immediately via hibernate unless I started the server (tomcat) again.
This is because your transaction in phpMyAdmin was not committed.
Try running this query in phpMyAdmin before running commands.
SET ##AUTOCOMMIT = 1;
Or running commit; at the end of your query.
Possible duplicate of:
COMMIT not working in phpmyadmin (MySQL)
I noticed that i've forgot to add transaction.commit(); for every hibernate session.get(); method, so somehow it keeps data in the cache.
public List<User> getAllUsers(User user) throws Exception {
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Criteria c = session.createCriteria(User.class).add(Restrictions.ne("idUser", user.getIdUser()));
List<User> users = c.list();
tx.commit();//i forget to add this
return users;
} catch (Exception e) {
if (tx != null) tx.rollback(); throw e;
} finally {
session.close();
}
}
We have been using annotation based JPA with Hibernate as JpaVendorAdapter.
We had to schedule a job to refresh an entire table data using spring scheduler.
The code is as follows,
#Scheduled(fixedDelay=120000)
public void refreshTable() {
EntityManager em = null;
try {
EntityManagerFactory emf = entityManager.getEntityManagerFactory();
em = emf.createEntityManager();
em.getTransaction().begin();
/// do something like delete and fill table
// this block is done for the sake of batch mode operation
em.getTransaction().commit();
} catch (Exception e) {
logger.error("Failed to refresh table", e);
} finally{
if(em != null && em.getTransaction().isActive()){
em.getTransaction().rollback();
logger.info("Failure while refreshing table, rolling back transaction.");
}
}
}
This used to build memory utilization and caused application hang.
We added, at the end of finally block,
if(em != null){
em.close();
}
Which solved the memory problem.
So, why does not EntityManager execute close() while being GC'ed?
The JPAConnector has various connections associated with it and it does not close all of them whereas waiting for garbage collector to do it is not a wise approach.
Closing the connections(i.e EntityManager and EntityManagerFactory) as and when they are no more needed is the best way to address this issue.
Hope this helps!
Good luck!