JPA中的事务回滚永远不会起作用

时间:2017-07-29 15:49:55

标签: java mysql jpa transactions

我有一段示例代码:

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();
    }
}

数据库是MySQL。 代码将Event实体持久存储到数据库中,然后抛出异常。我希望tx.rollback()删除对数据库所做的更改,但是这个命令永远不会有效,数据仍保留在表中:

enter image description here

问题是为什么tx.rollback()失败以及如果事务抛出异常,如何删除数据库中所做的更改?

更新: 的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>

4 个答案:

答案 0 :(得分:2)

将此添加到您的persistence.xml

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

答案 1 :(得分:1)

正如其他用户所说,这可能是一个自动提交问题,因为根据MySQL documentation

  

在InnoDB ......   默认情况下,MySQL会为每个新连接启动会话   autocommit enabled,所以MySQL在每个SQL语句后执行提交   该声明未返回错误。如果一个语句返回一个   错误,提交或回滚行为取决于错误。看到   第14.21.4节“InnoDB错误处理”。

此外,您不应将Transaction对象存储在变量中 每次要调用Transaction方法时,请从Transaction获取EntityManager对象。

所以替换:

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

by:

em.getTransaction().begin();

tx.rollback();替换为em.getTransaction().rollback();

Transaction中存储的EntityManager对象可能会被序列化,因此在事务处理过程中会有新的引用。

例如,查看AbstractEntityManagerImpl的序列化方法:

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

答案 2 :(得分:1)

也许启用了autocommit? MySQL手册指出默认情况下启用自动提交。

如果问题出现了,你肯定不会是第一个偶然发现的问题; - )

答案 3 :(得分:0)

不回滚事务的问题是由MyISAM引起的。使用InnoDB,回滚效果很好。