Thursday, December 4, 2008

How to test Java Persistence API in 5 minutes

I am the author of HiberObjects and want to give an example of how useful it can be.

Yesterday, someone asked me a Java Persistence API (JPA) question: If an object is removed from a bidirectional relation, do both objects have to be saved to update the database correctly?

I thought I knew the answer, but better test it first. This is how I did it:

1) Design a class diagram with 2 persistent classes User and Membership and 1 unit test UserPersistentTest:



2) Initialize the database with some objects that I can test against: Right-click on the UserPersistentTest class and select Design Objects, then design objects like this:



3) Save the diagram. This will generate JPA classes, a unit test and a helper class. The unit test will initialize the database with objects like this:
   private void initObjects()
{
EntityManager entityManager = persistenceHelper.getEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
user = new User();
membership = new Membership();
user.setName("Lars");
membership.setX(100);
entityManager.persist(user);
entityManager.persist(membership);
user.getMembership().add(membership);
membership.getUser().add(user);
tx.commit();
entityManager.close();
}
4) Implement the test method:
   public void test1() throws Exception
{
EntityManager em = persistenceHelper.getEntityManager();
em.getTransaction().begin();
User user1 = (User) em.createQuery("from User").getSingleResult();
assertEquals(1, user1.getMembership().size());

Membership membership1 = (Membership) em.createQuery("from Membership").getSingleResult();
assertEquals(1, membership1.getUser().size());

assertSame(membership1, user1.getMembership().iterator().next());
assertSame(user1, membership1.getUser().iterator().next());

user1.removeMembership(membership1);
assertEquals(0, user1.getMembership().size());
assertEquals(0, membership1.getUser().size());
em.getTransaction().commit();

em.getTransaction().begin();
User user2 = (User) em.createQuery("from User").getSingleResult();
assertEquals(0, user2.getMembership().size());
Membership membership2 = (Membership) em.createQuery("from Membership").getSingleResult();
assertEquals(0, membership2.getUser().size());
em.getTransaction().commit();
}
5) Run as JUnit test:



This demonstrates the answer to the question: You don't need to save any of the objects explicitly if you change them in a transaction.

2 comments:

Peter Bona said...

What is persistenceHelper in the code?

Lars said...

It is a helper class that is generated automatically by HiberObjects. It looks like this:

public class HibernateHelper
{
private static HibernateHelper singleton = new HibernateHelper();

private EntityManagerFactory factory;
private ThreadLocal currentEntityManager = new ThreadLocal();

private HibernateHelper()
{
}

public static void main(String[] args) throws Exception
{
String sqlFile = null;
if (args.length > 0)
{
sqlFile = args[0];
}
boolean print = (sqlFile == null);
boolean export = (sqlFile == null);
Configuration config = getInstance().getConfiguration();
SchemaExport exporter = new SchemaExport(config);
if (sqlFile != null)
{
exporter.setOutputFile(sqlFile);
}
exporter.create(print, export);
}

public static HibernateHelper getInstance()
{
return singleton;
}

public synchronized EntityManagerFactory getFactory()
{
if (factory == null)
{
factory = Persistence.createEntityManagerFactory("bidirectional-associations");
}
return factory;
}

public synchronized void close()
{
closeEntityManager();
if (factory != null)
{
factory.close();
factory = null;
}
}

public Configuration getConfiguration() throws MappingException
{
AnnotationConfiguration config = new AnnotationConfiguration();
config.addAnnotatedClass(User.class);
config.addAnnotatedClass(Membership.class);
return config;
}

public EntityManager getEntityManager()
{
EntityManager entityManager = (EntityManager) currentEntityManager.get();
if (entityManager == null || !entityManager.isOpen())
{
entityManager = getFactory().createEntityManager();
currentEntityManager.set(entityManager);
}
return entityManager;
}

public void closeEntityManager()
{
EntityManager entityManager = (EntityManager) currentEntityManager.get();
if (entityManager != null && entityManager.isOpen())
{
entityManager.close();
}
currentEntityManager.set(null);
}

public String toString()
{
return "HibernateHelper";
}
}