JPA - 事务未被提交

3

我正在开发一个项目,第一次使用JPA、Hibernate等技术。我遇到了事务未能提交的问题。我使用了下面这个类User:

 package org.tomasherman.JBTBackup.Resource.Entity;

import javax.persistence.*;
import java.io.Serializable;



@Entity
@Table(name = "users")
public class User implements Serializable {
    @Id
    @GeneratedValue
    private int id;

    private String login;
    private String email;
    private String password;
    private int credit;

    public User() {
    }

    public User(String login, String email, String password, int credit) {
        setLogin(login);
        setEmail(email);
        setPassword(password);
        setCredit(credit);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getCredit() {
        return credit;
    }

    public void setCredit(int credit) {
        this.credit = credit;
    }
}

使用HSQL数据库将其持久化到内存表中。 persistence.xml文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>

<persistence-unit name="JBTBackupPersistance">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>org.tomasherman.JBTBackup.Resource.Entity.User</class>
    <properties>
        <property name="hibernate.connection.url" value="jdbc:hsqldb:file:./database/database.hsql"/>
        <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
        <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
        <property name="hibernate.connection.username" value="SA"/> <!--default hsql login-->
        <property name="hibernate.connection.password" value=""/>
        <property name="hibernate.archive.autodetection" value="class"/>
        <property name="hibernate.show_sql" value="true"/>
        <property name="hibernate.format_sql" value="true"/>
        <property name="connection.shutdown" value="true"/>
    </properties>
</persistence-unit>

并在类似于以下的JUnit测试中进行测试:

package org.tomasherman.JBTBackup.Resource.Entity;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

/**
 * Created by IntelliJ IDEA.
 * User: arg
 * Date: Jun 29, 2010
 * Time: 11:24:35 PM
 * To change this template use File | Settings | File Templates.
 */

public class UserTest {
    private EntityManagerFactory emf;
    private EntityManager em;

    private final User u1 = new User("and","now","for",1234);
    private final User u2 = new User("something","completely","different",123123123);
    private final User u3 = new User("a","man","with",123123123);

    @Before
    public void initEmfAndEm() {
        emf = Persistence.createEntityManagerFactory("JBTBackupPersistance");
        em = emf.createEntityManager();
    }

    @After
    public void cleanup() {
        em.close();
    }

    @Test
    public void emptyTest() {
        System.out.println(em.createQuery("from User").getResultList().size());
        if(em.getTransaction().isActive()){
            System.out.println("FFfffffffffFFFFUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU");
            System.out.println("FFfffffffffFFFFUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU");
            System.out.println("FFfffffffffFFFFUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU");
            System.out.println("FFfffffffffFFFFUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU");
            System.out.println("FFfffffffffFFFFUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU");
        }else{
            em.getTransaction().begin();
            em.persist(u1);
            em.persist(u2);
            em.persist(u3);         
        em.getTransaction().commit();
        }
        System.out.println(em.createQuery("from User").getResultList().size());

    }
}

现在的问题是插入操作经常无法写入数据库。测试的输出结果如下:

    ...some hibernate stuff...
Hibernate: 
    select
        user0_.id as id0_,
        user0_.credit as credit0_,
        user0_.email as email0_,
        user0_.login as login0_,
        user0_.password as password0_ 
    from
        users user0_
20
Hibernate: 
    insert 
    into
        users
        (id, credit, email, login, password) 
    values
        (null, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        users
        (id, credit, email, login, password) 
    values
        (null, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        users
        (id, credit, email, login, password) 
    values
        (null, ?, ?, ?, ?)
Hibernate: 
    select
        user0_.id as id0_,
        user0_.credit as credit0_,
        user0_.email as email0_,
        user0_.login as login0_,
        user0_.password as password0_ 
    from
        users user0_
23

因此,实体存储在数据库的内存中,但是在单元测试后检查数据库时,没有数据。然而,它会不时地提交。

如果有人能帮我,那就太棒了。

编辑: 如果对任何人有帮助,这里有一个库版本列表(以Maven格式):

<dependency>
      <groupId>javax.transaction</groupId>
      <artifactId>jta</artifactId>
      <version>1.1</version>
  </dependency>
  <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.6.0</version>
  </dependency>
  <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>20040616</version>
  </dependency>
  <dependency>
      <groupId>org.hsqldb</groupId>
      <artifactId>hsqldb</artifactId>
      <version>2.0.0</version>
  </dependency>
  <dependency>
      <groupId>hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>3.4.0.GA</version>
  </dependency>
  <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.6.0</version>
  </dependency>
  <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-search</artifactId>
      <version>3.1.0.GA</version>
  </dependency>
  <dependency>
      <groupId>hibernate</groupId>
      <artifactId>hibernate</artifactId>
      <version>3.0.5</version>
  </dependency>
  <dependency>
      <groupId>javax.persistence</groupId>
      <artifactId>persistence-api</artifactId>
      <version>1.0</version>
  </dependency>

更新2: 另一个有趣的事情是,如果我在测试的结尾添加Thread.sleep(1000);,所有的事务都会被提交。这可能是因为程序完成得比数据库更新快吗?

2个回答

2
根据HSQLDB用户指南:

关闭数据库

从1.7.2版本开始,当最后一个JDBC连接显式关闭时[并且数据未写入磁盘],进程内的数据库不再关闭,需要使用显式的SHUTDOWN命令[以保持数据持久性]。在1.8.0中,可以在第一个连接到数据库的连接(打开数据库的连接)上指定一个连接属性shutdown=true,以在最后一个连接关闭时强制关闭。

因此,要么通过本机查询发送SHUTDOWN命令,要么将;shutdown=true添加到连接字符串中:
jdbc:hsqldb:file:./database/database.hsql;shutdown=true

关于`write_delay`的问题,不,将其设置为`0`在单元测试环境中不会导致问题(只会影响性能)。实际上,根据文档,我认为设置`shutdown=true`就足够了:

应用程序开发和测试

...

如果您不想运行服务器实例,并且需要在不同进程中的测试之间保持持久性,则应使用file:数据库。您可以使用shutdown=true连接属性确保在关闭连接后完全保留数据库。另一种选择是使用hsqldb.write_delay=false连接属性,但这比另一个选项稍慢。

有报道称,一些数据访问框架在测试结束后没有关闭所有与数据库的连接。在这种情况下,如果您希望数据在测试结束时保持持久性,则需要使用零写延迟。

但我猜您正在面对第二段描述的情况。虽然如此,我怀疑数据访问层是否真的有错。


Nit: 我认为shutdown=true只适用于HSQLDB >= 1.8,但答案是正确的,根据sourceforge上的文档:http://hsqldb.sourceforge.net/doc/guide/ch01.html#N101DB。 - Mike
嗯,我根据你的建议更新了JDBC URL,但没有任何改变,此外我认为 <property name="connection.shutdown" value="true"/> 与你建议的相等。不过还是感谢你的帖子。 - Arg
@Arg 哎呀!我没注意到 hibernate.connection.shutdown。你在之前的 hsqldb 版本(1.8.0.10)中是否遇到了相同的问题?由于你正在使用 maven,测试应该很容易。我无法重现这个问题。 - Pascal Thivent
其实我想我已经弄清楚了。在 database.hsql.script 中设置了 "SET FILES WRITE DELAY 500",我将其更改为 "SET FILES WRITE DELAY 0",现在似乎可以正常工作了。您认为这可能会引起问题吗? - Arg

0

不要两次调用em.getTransaction(),你需要只调用一次并保存对它的引用。每次调用都会得到一个新事务,所以你开始一个,但是提交了另一个。


这是不正确的。第二次调用会给你当前的事务。代码是正确的。 - Pascal Thivent
是的,我实际上尝试了保存交易和使用代码中显示的方法,但没有任何区别,不过还是谢谢您的帖子:] - Arg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接