views:

80

answers:

2

Hey guys,

i'm working on a project in which i use JPA, Hibernate and all this stuff for the first time and i ran into problem with transactions not being commited. I use class User which looks like this:

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

and use HSQL database to persist it in MEMORY table. The persistence.xml file looks like this:

<?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>

and test it in JUnit test which looks like this:

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

    }
}

Now the problem is that the insert is quite often not written into the database. The output of test looks like this:

    ...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

so the entites are in the memory of the database, but when i check the database after the unit test, there are no data. It, however, gets committed from time to time.

If anyone could help me, it would be fantastic.

EDIT: in case it would help anyone, there is a list of libraries versions(in maven format):

<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: Another interesting thing, it seems that if i add Thread.sleep(1000); at the end of the test, all transactions get committed. Could it be that the program finishes faster then the database can be update?

+1  A: 

According to the HSQLDB user guide:

Closing the Database

From version 1.7.2, in-process databases are no longer closed when the last connection to the database is explicitly closed via JDBC [and data are not written to disk], an explicit SHUTDOWN command is required [in order to keep data persistent]. In 1.8.0, a connection property, shutdown=true, can be specified on the first connection to the database (the connection that opens the database) to force a shutdown when the last connection closes.

So either send that SHUTDOWN command through a native query or add ;shutdown=true to the connection string:

jdbc:hsqldb:file:./database/database.hsql;shutdown=true

Regarding the question about write_delay, no, setting it to 0 won't cause a problem in a unit testing context (it "just" has an impact on performances). Actually, I would have expect shutdown=true to be enough as mentioned in the documentation:

Application Development and Testing

...

If you do not want to run a Server instance, and you need persistence between tests in different processes, then you should use a file: database. You can use the shutdown=true connection property to ensure the database is persisted fully after the connections are closed. An alternative option is to use hsqldb.write_delay=false connection property, but this is slightly slower than the other option.

It has been reported that some data access frameworks do not close all their connection to the database after the tests. In such situations, you need to use zero WRITE DELAY if you want the data to persist at the end of the tests

But I guess that you are facing the situation described in the second paragraph. I wonder if the data access layer is really guilty though.

Pascal Thivent
Nit: I think shutdown=true will only work with HSQLDB >= 1.8, but the answer is correct, per documentation on sourceforge : http://hsqldb.sourceforge.net/doc/guide/ch01.html#N101DB.
Mike
@Mike Thanks. Fixed.
Pascal Thivent
hmm i updated the jdbc url according to your suggestion but it made no difference, besides i thought the <property name="connection.shutdown" value="true"/>is equal to what you suggested. Thanks for the post though
Arg
@Arg Doh! I didn't spot the `hibernate.connection.shutdown`. Do you get the same behavior with the previous version of hsqldb (1.8.0.10)? Since you're using maven, it should be pretty easy to test. I can't reproduce the problem.
Pascal Thivent
Actually i think i figured it out. In the database.hsql.script was setting "SET FILES WRITE DELAY 500", i changed it to "SET FILES WRITE DELAY 0" and it seems to be working now. Do you think that this could cause a problem?
Arg
A: 

Instead of calling em.getTransaction() twice, you need to call it once, and save the reference to it. Each time you call it, you get a new transaction. So you start one, but then commit a separate one.

Andy
This is not true. The second call gives you the **current** transaction. The code is correct.
Pascal Thivent
yeah, i actually tried both saving the transaction and using the approach showed in the code and it made no difference, thanks for the post though :]
Arg