sva605
sva605

Reputation: 1681

Transaction rollback in JPA never works

I have a piece of example code:

public class JpaTest {
    private EntityManagerFactory emf;

    private void setUp() throws Exception {
        emf = Persistence.createEntityManagerFactory("testPU");
    }

    private void tearDown() {
        emf.close();
    }

    private void save() {
        EntityManager em = null;
        EntityTransaction tx = null;
        try {
            em = emf.createEntityManager();
            tx = em.getTransaction();
            tx.begin();
            em.persist(new Event("First event", new Date()));
            em.persist(new Event("A follow up event", new Date()));
            throw new RuntimeException();
        } catch (Exception e) {
            if (tx != null) tx.rollback();
        } finally {
            if (em != null) em.close();
        }
    }

    public static void main(String[] args) throws Exception {
        JpaTest test = new JpaTest();
        test.setUp();
        test.save();
        test.tearDown();
    }
}

The database is MySQL. The code persists Event entity into the database and than throws an Exception. I expect tx.rollback() to delete the changes made into the database, but this command never works and the data remains in the table:

enter image description here

The question is why tx.rollback() fails and how to delete changes made in the database if transaction throws an Exception?

UPDATED: persistence.xml:

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">

    <persistence-unit name="testPU">

        <class>exampleForTestingJpa.Event</class>

        <properties>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url"
                      value="url here..."/>
            <property name="javax.persistence.jdbc.user" value="username here..."/>
            <property name="javax.persistence.jdbc.password" value="password here..."/>

            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.connection.autocommit" value="false"/>
        </properties>
    </persistence-unit>
</persistence>

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>groupId</groupId>
    <artifactId>example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>4.3.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.2.9.Final</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.39</version>
        </dependency>
    </dependencies>

</project>

Upvotes: 0

Views: 2290

Answers (4)

sva605
sva605

Reputation: 1681

The problem with not rolling transaction back was caused by MyISAM. With InnoDB rollback works fine.

Upvotes: 0

davidxxx
davidxxx

Reputation: 131326

As other users said, it is probably a auto-commit issue since according to the MySQL documentation :

In InnoDB ... By default, MySQL starts the session for each new connection with autocommit enabled, so MySQL does a commit after each SQL statement if that statement did not return an error. If a statement returns an error, the commit or rollback behavior depends on the error. See Section 14.21.4, “InnoDB Error Handling”.

Besides, you should not store the Transaction object in a variable.
At each time you want to invoke a Transaction method, get the Transaction object from the EntityManager.

So replace :

tx = em.getTransaction();
tx.begin();

by :

em.getTransaction().begin();

And replace tx.rollback(); by em.getTransaction().rollback();

The Transaction object stored in the EntityManager may be serialized and so have a new reference during transaction processing.

For example, look at the serialization method of AbstractEntityManagerImpl:

public class org.hibernate.jpa.spi.AbstractEntityManagerImpl{
...
  private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    ois.defaultReadObject();
    tx = new TransactionImpl( this );
  }
...
}

Upvotes: 1

jausen brett
jausen brett

Reputation: 1118

Maybe autocommit is enabled ? MySQL manual states that autocommit is enabled by default.

If thats the problem, you surely will not be the first who stumbled over that ;-)

Upvotes: 1

fg78nc
fg78nc

Reputation: 5232

Add this to your persistence.xml

<property name="hibernate.connection.autocommit" value="false"/>

Upvotes: 2

Related Questions