In the good spirit of open source, any Hibernate ORM issue should be accompanied by a replicating test case. The test case is a proof that the issue really exists and is reproducible.
To simplify the test case writing procedure, Hibernate provides a series of templates that you can just grab from GitHub. Thanks to these tests, the issue reporter can focus on the actual persistence-related problem since the templates take care of all the bootstrapping logic.
Previously, the test case templates were available only for the Hibernate native API, which was fine as long as you’re familiar with it. Because many projects use Hibernate as a JPA provider, it’s very convenient to offer a JPA bootstrap environment as well. And that’s what we did.
Next, I’m going to provide a step-by-step guide for writing a JPA-based Hibernate test case.
First, you need to either fork or download the hibernate-test-case-templates GitHub repository.
This repository has an orm
folder containing two Maven modules:
- hibernate-orm-4
-
To replicate issues for Hibernate 4.x
- hibernate-orm-5
-
To replicate issues for Hibernate 5.x
Each module has three templates:
- ORMStandaloneTestCase.java
-
This one is a Hibernate native test case (you get access to the
SessionFactory
and you operate with aSession
), but it requires you to manually bootstrap the Hibernate environment. - ORMUnitTestCase.java
-
This is also a Hibernate native test case (you get access to the
SessionFactory
and you operate with aSession
), but the bootstrapping is provided on your behalf. - JPAUnitTestCase.java
-
This is the new template that needs to be used for replicating an issue using the Java Persistence API (you get access to the
EntityManagerFactory
and you operate with anEntityManager
).
When replicating an issue with the Hibernate native API, the |
This post focuses on the newly added JPAUnitTestCase
which looks like this:
public class JPAUnitTestCase {
private EntityManagerFactory entityManagerFactory;
@Before
public void init() {
entityManagerFactory = Persistence.createEntityManagerFactory( "templatePU" );
}
@After
public void destroy() {
entityManagerFactory.close();
}
// Entities are auto-discovered, so just add them anywhere on class-path
// Add your tests, using standard JUnit.
@Test
public void hhh123Test() throws Exception {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// Do stuff...
entityManager.getTransaction().commit();
entityManager.close();
}
}
The EntityManagerFactory
is created before every test case and destroyed afterwards.
The test case logic goes inside the @Test
Junit method.
You should name the test method after the Hibernate JIRA issue you are trying to replicate. |
The EntityManagerFactory
uses the templatePU
Persistence Unit, which is located under src/test/resources/META-INF/persistence.xml
.
By default, this file looks like this:
<persistence 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"
version="2.1">
<persistence-unit name="templatePU" transaction-type="RESOURCE_LOCAL">
<description>Hibernate test case template Persistence Unit</description>
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.archive.autodetection" value="class, hbm"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.connection.url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1"/>
<property name="hibernate.connection.username" value="sa"/>
<property name="hibernate.connection.pool_size" value="5"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.max_fetch_depth" value="5"/>
<property name="hibernate.cache.region_prefix" value="hibernate.test"/>
<property name="hibernate.cache.region.factory_class"
value="org.hibernate.testing.cache.CachingRegionFactory"/>
<!--NOTE: hibernate.jdbc.batch_versioned_data should be set to false when testing with Oracle-->
<property name="hibernate.jdbc.batch_versioned_data" value="true"/>
<property name="javax.persistence.validation.mode" value="NONE"/>
<property name="hibernate.service.allow_crawling" value="false"/>
<property name="hibernate.session.events.log" value="true"/>
</properties>
</persistence-unit>
</persistence>
The |
All entities are auto-discovered, so you can place them anywhere on classpath. In this example, we are going to use the following entity:
@Entity
public class Event {
@Id
@GeneratedValue
private Long id;
@Temporal(TemporalType.TIMESTAMP )
private Date createdOn;
public Event() {
}
public Event(Date createdOn) {
this.createdOn = createdOn;
}
public Long getId() {
return id;
}
public Date getCreatedOn() {
return createdOn;
}
}
Now, the persistence logic can be added to the JUnit test method:
@Test
public void hhh123Test() throws Exception {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
Event event = new Event( new Date() );
entityManager.persist( event );
Event dbEvent = entityManager.createQuery(
"select e " +
"from Event e", Event.class)
.getSingleResult();
assertEquals(event.getCreatedOn(), dbEvent.getCreatedOn());
entityManager.getTransaction().commit();
entityManager.close();
}
That’s it! You can now provide a Hibernate test case using the standard Java Persistence API.