6
votes

I am developing a java web application using Hibernate and MySQL. Sometimes when I try to persist things, I get this rollback exception:

javax.persistence.RollbackException: Error while committing the transaction
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:94)
    at com.tulips.dao.CommentDao.saveOrUpdate(CommentDao.java:42)
    at com.tulips.servlets.CommentsServlet.doPost(CommentsServlet.java:168)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:315)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException
    at org.hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:155)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:91)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:55)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77)
    ... 23 more

here is my persistence.xml file :

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0">

    <persistence-unit name="licenta_pu"
        transaction-type="RESOURCE_LOCAL">
<!--        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> -->
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <properties>
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/tulips"/>
            <property name="hibernate.connection.username" value="...." />
            <property name="hibernate.connection.password" value="...." />
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
            <property name="hibernate.show-sql" value="false" />
<!--            <property name="hibernate.jdbc.batch_size" value="1000" /> -->
            <!-- <property name="hibernate.hbm2ddl.auto" value="update" /> -->          
        </properties>
    </persistence-unit>
</persistence>

I've tried changing the hibernate provider but that didn't help :( [org.hibernate.ejb.HibernatePersistence and org.hibernate.jpa.HibernatePersistenceProvider]

I don't know exactly how to replicate this error, sometimes I get it, sometimes I don't. I have observed that every time after I restart my computer and run the project in Eclipse, and try to persist data, it works. If I stop the project, eventually add some data into the database via MySQL Workbench, and then run the project again, I get the error.

Here is my EntityManagerHolder:

public class EntityManagerHolder {

    private static EntityManagerHolder INSTANCE = new EntityManagerHolder();
    private EntityManagerFactory emf;
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public static EntityManagerHolder getInstance() {
        return INSTANCE;
    }

    private EntityManagerHolder() {
        super();
        emf = Persistence
                .createEntityManagerFactory("licenta_pu");
        entityManager = emf.createEntityManager();
    }

}

Here is my GenericDao:

import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import com.tulips.model.AbstractEntity;

@Transactional
public abstract class GenericDao {

    protected EntityManager em;

    public GenericDao(EntityManager em) {
        this.em = em;
    }

    public void saveOrUpdate(AbstractEntity e) {
        if (e == null) {
            throw new NullPointerException("Trying to persist a null object");
        }
        em.getTransaction().begin();
        if (e.getId() == null) {
            em.persist(e);
        } else {
            em.merge(e);
        }
        em.getTransaction().commit();
    }


}

I'm using the following Hibernate libraries:

        mysql-connector-java-5.1.9.jar;
        hibernate-commons-annotations-4.0.4.Final.jar;
        hibernate-core-4.3.4.Final.jar;
        hibernate-entitymanager-4.3.4.Final.jar;
        hibernate-jpa-2.1-api-1.0.0.Final.jar;
1

1 Answers

16
votes

The problem might be that you have a single EntityManager shared by all Web requests. EntityManager is not thread safe. The standard approach in a webapp is to create a new instance of EntityManager for each request. It is ok to have a shared EntityManagerFactory, but not a shared EntityManager.

From the Hibernate doc:

An EntityManager is an inexpensive, non-threadsafe object that should be used once, for a single business process, a single unit of work, and then discarded. An EntityManager will not obtain a JDBC Connection (or a Datasource) unless it is needed, so you may safely open and close an EntityManager even if you are not sure that data access will be needed to serve a particular request.