Unit test JPA Entities with in-memory Derby/JavaDB

HibernateAbout two years ago I blogged about using HSQLDB to unit test JPA entities. This year, Apache released a Derby version allowing you to use an in memory backend. As I use Derby in software I write, being able to run unit tests on the very same SGBD but in memory is a real gift.

For reference, here is a link to my previous post titled Unit test JPA Entities with in-memory database. What follows is just the very same method applied to Derby.


Dependencies

Here are the dependencies I added to my project with the “test” scope :

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>3.3.2.GA</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.apache.derby</groupId>
    <artifactId>derby</artifactId>
    <version>10.5.3.0</version>
    <scope>test</scope>
</dependency>

Persistence Unit

Here the interesting snippet of my META-INF/persistence.xml file :

<persistence-unit name="testPU" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>myapp.model.entities.Group</class>
    <class>myapp.model.entities.User</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
        <property name="hibernate.connection.url" value="jdbc:derby:memory:unit-testing-jpa"/>
        <property name="hibernate.connection.driver_class" value="org.apache.derby.jdbc.EmbeddedDriver"/>
        <property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect"/>
        <property name="hibernate.hbm2ddl.auto" value="create"/>
        <property name="hibernate.connection.username" value=""/>
        <property name="hibernate.connection.password" value=""/>
    </properties>
</persistence-unit>

The unit test

Here is a simple complete test case :

 public class PersistenceUnitTest extends TestCase {

    private static Logger logger = Logger.getLogger(PersistenceUnitTest.class.getName());

    private EntityManagerFactory emFactory;

    private EntityManager em;

    public PersistenceUnitTest(String testName) {
        super(testName);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        try {
            logger.info("Starting in-memory database for unit tests");
            Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
            DriverManager.getConnection("jdbc:derby:memory:unit-testing-jpa;create=true").close();
        } catch (Exception ex) {
            ex.printStackTrace();
            fail("Exception during database startup.");
        }
        try {
            logger.info("Building JPA EntityManager for unit tests");
            emFactory = Persistence.createEntityManagerFactory("testPU");
            em = emFactory.createEntityManager();
        } catch (Exception ex) {
            ex.printStackTrace();
            fail("Exception during JPA EntityManager instanciation.");
        }
    }

    @Override
    protected void tearDown() throws Exception {
        super.tearDown();
        logger.info("Shuting down Hibernate JPA layer.");
        if (em != null) {
            em.close();
        }
        if (emFactory != null) {
            emFactory.close();
        }
        logger.info("Stopping in-memory database.");
        try {
            DriverManager.getConnection("jdbc:derby:memory:unit-testing-jpa;shutdown=true").close();
        } catch (SQLNonTransientConnectionException ex) {
            if (ex.getErrorCode() != 45000) {
                throw ex;
            }
            // Shutdown success
        }
        VFMemoryStorageFactory.purgeDatabase(new File("unit-testing-jpa").getCanonicalPath());
    }

    public void testPersistence() {
        try {

            em.getTransaction().begin();

            User u = new User();
            u.setEmail("eskatos@yopmail.com");
            u.setFirstName("eskatos");
            u.setLastName("YOP");
            u.setOrganisation("Tagada");

            em.persist(u);
            assertTrue(em.contains(u));

            Group g = new Group();
            g1.addUser(u);

            em.persist(g);
            assertTrue(em.contains(g));

            g.removeUser(u);
            em.remove(u);
            em.merge(g);
            assertFalse(em.contains(u));

            em.getTransaction().commit();

        } catch (Exception ex) {
            em.getTransaction().rollback();
            ex.printStackTrace();
            fail("Exception during testPersistence");
        }
    }
}

Et voila, now with Derby :) !

Advertisements

9 thoughts on “Unit test JPA Entities with in-memory Derby/JavaDB

  1. Thanks for the information, eskatos.
    Great to see the in-memory back end being used :)

    I just want to point out that the way to delete Derby / Java DB in-memory databases (and possibly other types of databases) will change in the next feature release. The current mechanism I’m looking into is using a connection URL property, like “jdbc:derby:memory;mydb;delete=true[;user=…;password=…]”.

    If anyone has feedback on the current implementation or wishes for future features, feel free to share it with the community on the Apache Derby user’s mailing list. The in-memory database back end in Derby 10.5 is considered experimental, so this is the time to raise your opinion if you want to see major compatibility breaking changes!

    Regards,

  2. Great post! I have a question, though. I am planning to use it for unit testing seam ejb using maven. Where do I put META-INF/persistence.xml file? Under \test\resources directory? Thank you in advance.

  3. Hi,

    I did exactly the same with Derby for inmemory testing and encapsulated it into a framework. The framework allows inmemory testing as well as connecting to a third-party framework.

    Have a look at http://utils.wamblee.org/test/enterprise/index.html, and in particular at http://utils.wamblee.org/test/enterprise/apidocs/org/wamblee/test/persistence/package-summary.html

    I am developing this as open source and it is available on the central maven repo.

    Cheers
    Erik

  4. if you have sql-type=”number” in your mapping files, HSQLDB or Derby doesnt like it. either remove it or change it to “numeric”.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s