Posts Tagged ‘jpa’

Recently I was playing a little with Hades. Generic DAO implementation which can reduce writing a lot of boiler plate code. So far so good, but I wanted to test some mappings with bidirectional relationship using automated tests. The feature of Hibernate (and JPA implementations in general) is an ability to cache some operations in Session/EntityManager (the first level cache) reducing number of statement sending to database. Generally it’s useful, but when you try to test some DAO operations with rollback at the end of a test (instead of commit) it complicates a few things. Sample situation, persist some object. Hibernate memorize that object should be created in database, but waits with sending insert statement on flush, commit (which causes flush) or related query which needs to have that data in database to perform (like findAll). Using rollback causes that there could be no flush (all read/write operations are performed within EntityManager) and some problems which occur in normal usage from end application could remain undiscovered.
The first idea how to solve that is to call flush on EntityManager, but DAO interface in Hades (and hopefully any other custom made generic DAO) doesn’t offer that command. It could be quite dangerous to give a developer an ability to call flush whenever he/she likes in the application (at least it could have the negative performance impact). Ok, the second idea is to cast interface to it’s implementation. Even in Hades implementation has protected getEntityManager method to allow to use EntityManager directly in specified DAOs. Let’s play. Small utility class in the same package, cast DAO interface to GenericDaoSupport and…

java.lang.ClassCastException: $Proxy38 cannot be cast to org.synyx.hades.dao.orm.GenericJpaDao

Ups, something is wrong. The problem is that our DAO interface implementation autowired with our test is not directly GenericJpaDao, but proxy object made by Spring. It’s not even Hades specific issue. Similar situation could happen when home made generic DAO is used with Spring.

Looking at Spring proxy object in debugger shows it’s a quite complicated object and it wouldn’t be easy to “find” target object. What is more I try to not use dirty hacks when not needed, so I started to look for some other way to get target class using Spring mechanisms itself. After some digging I have found Advised interface which Spring proxy implements. With its help it is possible to do:

targetObject = ((Advised)daoInterface).getTargetSource().getTarget();

which can be without further problems cast to GenericJpaDao which offers getEntityManager. Having EntityManager it’s simple to do flush or detach on given entity. I have found it useful to create an util class offering mentioned functionality for the usage in DAO tests across the project.

package package.with.your.dao.implementation;

import required.classes;

/**
 * Utility class allowing to get EntityManager (to perform flush and/or detach) from DAO interface (available even as Spring proxy).
 *
 * Licensed under the terms of Apache Software License version 2.
 *
 * @author Marcin Zajączkowski, https://solidsoft.wordpress.com/
 */
public final class DaoTestUtil {

    private DaoTestUtil() {
    }

    public static EntityManager getEntityManagerFromSpringEnhancedDaoInterface(GenericDao<?, ?> daoInterface) {

        try {
            Object targetObject = null;
            if (daoInterface instanceof Advised) {
                //Spring proxy
                targetObject = ((Advised)daoInterface).getTargetSource().getTarget();
            } else {
                targetObject = daoInterface;
            }

            GenericJpaDao daoImpl = (GenericJpaDao)targetObject;
            return daoImpl.getEntityManager();
        } catch (Exception e) {
            throw new IllegalArgumentException("Unable to get GenericJpaDao from " + daoInterface.getClass().getName(), e);
        }
    }

    public static void doFlushAndDetachEntityUsingEntityManagerFromDao(GenericDao<?, ?> daoInterface, Object entityDoDetach) {
        EntityManager em = getEntityManagerFromSpringEnhancedDaoInterface(daoInterface);
        em.flush;
        em.detach(entityDoDetach);
    }
}