Rafcik
Rafcik

Reputation: 362

Using H2 Database In-Memory does not persist Objects in Unit Tests

I have a problem concerning JPA in persistence.xml for unit tests. While persisting objects to the database H2 in-memory an error occurs:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running some.class.MyClass
################################################################################
                    BeforeClass Start  
################################################################################
                    EntityManager is being created..  
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 1.541 sec <<< FAILURE!

Results :

Tests in error: 
  some.class.MyClass: getSingleResult() did not retrieve any entities

I am sure there's a problem with persisting, NOT with obtaining the data, because reading methods in DEV application work perfectly with real databases.

Here is my persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" 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">
    <persistence-unit name="TestPU" transaction-type="RESOURCE_LOCAL">

        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>all entities</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>

        <properties>
            <property name="eclipselink.logging.level" value="SEVERE"/>
            <!-- ALL SEVERE -->
            <property name="eclipselink.logging.level.sql" value="SEVERE"/>
            <!-- ALL SEVERE -->
            <property name="eclipselink.logging.parameters" value="true"/>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;MODE=Oracle;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS DEV"/>
            <property name="javax.persistence.jdbc.user" value="sa"/>
            <property name="javax.persistence.jdbc.password" value=""/>
            <!-- model creation have to be done via javax.persistence, not hibernate/eclipselink - otherwise import doesn't works -->
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
            <property name="javax.persistence.schema-generation.create-source" value="metadata"/>
            <property name="javax.persistence.schema-generation.drop-source" value="metadata"/>
        </properties>

    </persistence-unit>
</persistence>


</persistence-unit>

And here's a Java piece of code:

@BeforeClass
        public static void setUpClass() {
                myOut("BeforeClass Start");
                EM = Persistence.createEntityManagerFactory(PU).createEntityManager();
                myOut("EntityManager is being created..");
                EM.persist(new Account(PU, PU, PU));
                EM.persist(new Account(PU + "2", "dsaddsa", "dsadas"));
                EM.persist(new Account(PU + "3", "saddsd", "dsadasa"));
                EM.persist(new Account(PU + "4", "adsada", "hdsd"));

                final DaoLogged dao = new DaoLogged();
                dao.setEntityManager(EM);
                dao.getAccountByLogin(PU);
                myOut("BeforeClass Done.");
        }

SingleResult Method:

public Account getAccountByLogin(String login) {
                return (Account) getEntityManager().createNamedQuery("Account.findByLogin").setParameter("login", login).getSingleResult();
}

I will appreciate any help! :) Regards!

[EDIT]

I was looking for an answer pretty long, actually in today's morning this completely didn't work, but after some hours of editing, searching the Internet I stuck with this..

[EDIT 2]

I've changed above method into:

@BeforeClass
        public static void setUpClass() {
                myOut("BeforeClass Start");
                EM = Persistence.createEntityManagerFactory(PU).createEntityManager();
                myOut("EntityManager is being created..");

                **EM.getTransaction().begin();**

                EM.persist(new Account(PU, PU, PU));
                EM.persist(new Account(PU + "2", "dsaddsa", "dsadas"));
                EM.persist(new Account(PU + "3", "saddsd", "dsadasa"));
                EM.persist(new Account(PU + "4", "adsada", "hdsd"));
                **EM.getTransaction().commit();
                EM.flush();
                EM.getTransaction().begin();**
                final DaoLogged dao = new DaoLogged();
                dao.setEntityManager(EM);
                dao.getAccountByLogin(PU);
                myOut("BeforeClass Done.");
        }

Still errors, but diffrent:

javax.persistence.TransactionRequiredException: 

Exception Description: No transaction is currently active

Upvotes: 2

Views: 3535

Answers (2)

Rafcik
Rafcik

Reputation: 362

Okay, I spent some time having fun with these errors.. :)

  1. Transactions were necessary, in that case flush should be invoked before commit
  2. I forgot about adding a scope test to the eclipselink dependency in POM
  3. Lombok does not cause any errors
  4. Queries were well formed, in main project (so far I called this 'DEV' here) they didn't cause any error
  5. Sequence Generator is required in tests!

Upvotes: 0

Aaron Digulla
Aaron Digulla

Reputation: 328604

The error means that the query didn't return any results. Make sure that the database is set up correctly. For this, enable the DEBUG log level for EclipseLink. That should show you the SQL which is being sent to the database.

You also need to a transaction manager. How that is set up and configured depends on the architecture of your application. The transaction manager is responsible for orchestrating all the different participants of transactions (the database, the EntityManager and sometimes other parties like JMS).

If you use Spring, then the transaction manager will automatically synchronizes transactions with tests.

Note that you should try to start the transaction before each test and roll back after each test. That way, test #2 won't fail because test #1 left junk in the database.

Upvotes: 1

Related Questions