JPA - транзакции не совершаются - PullRequest
3 голосов
/ 08 июля 2010

Я работаю над проектом, в котором я впервые использую 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, чтобы сохранить ее в таблице MEMORY. Файл 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

поэтому объекты находятся в памяти базы данных, но когда я проверяю базу данных после модульного теста, данных нет. Это, однако, совершается время от времени.

Если бы кто-нибудь мог мне помочь, это было бы здорово.

EDIT: на случай, если это кому-нибудь поможет, есть список версий библиотек (в формате 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>

UPDATE2: Еще одна интересная вещь, кажется, что если я добавлю Thread.sleep (1000); в конце теста все транзакции фиксируются. Может ли быть так, что программа завершает работу быстрее, чем обновление базы данных?

Ответы [ 2 ]

2 голосов
/ 09 июля 2010

Согласно руководству пользователя 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 свойство соединения, но это немного медленнее, чем другой вариант.

Сообщалось, что некоторые инфраструктуры доступа к данным не закрывают все свои подключения к базе данных после тестов.В таких ситуациях вам нужно использовать нулевое ЗАДЕРЖКА ЗАПИСИ, если вы хотите, чтобы данные сохранялись в конце тестов

Но я предполагаю, что вы столкнулись с ситуацией, описанной во втором абзаце.Интересно, действительно ли виновен уровень доступа к данным.

0 голосов
/ 09 июля 2010

Вместо того, чтобы вызывать em.getTransaction() дважды, вам нужно вызвать его один раз и сохранить ссылку на него.Каждый раз, когда вы звоните, вы получаете новую транзакцию.Таким образом, вы начинаете один, а затем совершаете отдельный.

...