Search code examples
javahibernatejpaentitymanagerthread-local

Hibernate - ThreadLocal<EntityManager> vs EntityManager per operation


I use Hibernate 5.1.0.Final, Guice, Jersey. I have HibernateModule which creates EntityManagerFactory and manages EntityManager instances:

public class HibernateModule extends AbstractModule {

    private static final ThreadLocal<EntityManager> ENTITY_MANAGER_CACHE = new ThreadLocal<EntityManager>();

    @Provides @Singleton
    public EntityManagerFactory provideEntityManagerFactory(@Named("hibernate.connection.url") String url, 
        @Named("hibernate.connection.username") String userName, 
        @Named("hibernate.connection.password") String password,
        @Named("hibernate.hbm2ddl.auto") String hbm2ddlAuto,
        @Named("hibernate.show_sql") String showSql) {

        Map<String, String> properties = new HashMap<String, String>();
        properties.put("hibernate.connection.driver_class", "org.postgresql.Driver");
        properties.put("hibernate.connection.url", url);
        properties.put("hibernate.connection.username", userName);
        properties.put("hibernate.connection.password", password);
        properties.put("hibernate.connection.pool_size", "1");
        properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
        properties.put("hibernate.hbm2ddl.auto", hbm2ddlAuto);
        properties.put("hibernate.show_sql", showSql);
        properties.put("hibernate.cache.use.query_cache", "false");
        properties.put("hibernate.cache.use_second_level_cache", "false");

        return Persistence.createEntityManagerFactory("db-manager", properties);
    }

    @Provides
    public EntityManager provideEntityManager(EntityManagerFactory entityManagerFactory) {

        EntityManager entityManager = ENTITY_MANAGER_CACHE.get();

        if (entityManager == null || !entityManager.isOpen()) 
            ENTITY_MANAGER_CACHE.set(entityManager = entityManagerFactory.createEntityManager());

        entityManager.clear();

        return entityManager;
    }
}

entityManager.clear() is used to clear persistence context and force to query up-to-date data from a dabatase. GenericDAO receives injected EntityManager from HibernateModule. Main methods:

public class GenericDAOImpl<T> implements GenericDAO<T> {

    @Inject
    protected EntityManager entityManager;

    private Class<T> type;

    public GenericDAOImpl(){}

    public GenericDAOImpl(Class<T> type) {
        this.type = type;
    }

    @Override
    public void save(T entity) {
        entityManager.getTransaction().begin();
        entityManager.persist(entity);
        entityManager.getTransaction().commit();
    }

    @Override
    public T find(Long entityId) {
        return (T) entityManager.find(type, entityId);
    }
}

Previously I have tried to implemented a solution which provides new EntityManager on every DB operation but it lead to some side effects like "detached entity passed to persist".

Is it good practice to reuse EntityManager from ThreadLocal<EntityManager>? Are there any potential drawbacks of this implementation?


Solution

  • It should work correctly. Spring Framework is using the ThreadLocal class for the EntityManager. From reference:

    Spring makes it easy to create and bind a Session to the current thread transparently

    You should remember about JPA hints, PersistenceContextType and FlushModeType if you want to re-use EntityManager instance.