5

I am trying to integrate our DAO maven module to spring, we don't want to change any code of the dao. Previously, all the dao classes get entity manager using the following way.

Persistence.getEntityManager()

In spring I see lot of example using annotation to inject the entity manager but that need change in dao code. Is there a way I can modify the below class to make it work in spring?

 public class PersistenceManager
    {

        /**
         * Key that stores the entity manager.
         */
        private static final String ENTITY_MANAGER_INSTANCE = "ENTITY_MANAGER_INSTANCE";
        /**
         * Persistence unit for eMyEd
         */
        private static final String PERSISTENCE_UNIT_EMYED = "application_openjpa";
        /**
         * The static factory used across the JVM
         */
        private static final EntityManagerFactory ENTITY_MANAGER_FACTORY = Persistence
                .createEntityManagerFactory(PERSISTENCE_UNIT_EMYED);

        /**
         * Cleanup any entity managers created in the thread context.
         */
        public static void close()
        {
            EntityManager emInstance = getEntityManager();
            try
            {
                if (emInstance.isOpen())
                {
                    emInstance.close();
                }
            }
            catch (Throwable t)
            {
                // ignore
            }
            finally
            {
                ThreadContext.instance.clear();
            }
        }

        /**
         * Returns the Entity manager associated with the given thread context. If
         * none available, one is created and set on the thread context and the same
         * is returned.
         *
         * @return
         */
        public static EntityManager getEntityManager()
        {
            EntityManager emInstance = (EntityManager) ThreadContext.instance
                    .get(ENTITY_MANAGER_INSTANCE);
            if (emInstance == null)
            {
                emInstance = createEntityManager();
                emInstance.setFlushMode(FlushModeType.COMMIT);
                ThreadContext.instance.put(ENTITY_MANAGER_INSTANCE, emInstance);
            }
            try
            {
                // try to join the current active transaction
                emInstance.joinTransaction();
            }
            catch (TransactionRequiredException notSupportedEx)
            {
                // If there was no transaction to join. Ignore
            }
            catch (Throwable unknownEx)
            {
                // If there was no transaction to join. Ignore
            }
            return emInstance;

        }

        /**
         * Create a new entity manager.
         *
         * @return
         */
        private static EntityManager createEntityManager()
        {
            return ENTITY_MANAGER_FACTORY.createEntityManager();
        }
    }

Spring Configuration

<description>Example configuration to get you started.</description>
    <context:component-scan base-package="com.veera" />
 <!-- tell Spring that it should act on any @PersistenceContext and @Transactional annotations found in bean classes -->
    <tx:annotation-driven transaction-manager="transactionManager" />
    <!-- ******************************************************************** -->
    <!-- Setup the transaction manager -->
    <!-- ******************************************************************** -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>


    <!-- ******************************************************************** -->
    <!-- Setup each data source -->
    <!-- ******************************************************************** -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/test" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

    <!-- ******************************************************************** -->
    <!-- Setup each persistence unit -->
    <!-- ******************************************************************** -->
    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter">
                <property name="databasePlatform"
                    value="org.apache.openjpa.jdbc.sql.MySQLDictionary" />
            </bean>
        </property>
        <property name="persistenceUnitName" value="application_openjpa" />
        <!--  property name="loadTimeWeaver">
            <bean
                class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
        </property -->
        <!-- property name="jpaProperties"> <props> <prop key="hibernate.hbm2ddl.auto">create-drop</prop> 
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> 
            </props> </property -->
    </bean>
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

Persistence.xml

    <persistence-unit name="application_openjpa"
        transaction-type="RESOURCE_LOCAL">
        <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
        <class>com.veera.jpa.Item</class>
        <class>com.veera.jpa.Order</class>
        <class>com.zreflect.emyed.entity.user.User</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <!--  property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema" /
            <property name="openjpa.InitializeEagerly" value="true"/-->
            <property name="openjpa.Log" value="File=stdout, DefaultLevel=INFO, Runtime=INFO, SQL=TRACE"/>
        </properties>
    </persistence-unit>

</persistence>

2 Answers 2

1

As an EntityManager by definition is not threadsafe, the only - still ugly - solution is to hold an EntityManagerFactory statically and create EntityManager instances from the factory on request.

I'd even suggest to change the DAO to use dependency injection cause with this approach, your DAOs essentially become un-unittestable without bootstrapping the entire JPA infrastructure.

Sign up to request clarification or add additional context in comments.

1 Comment

As I already mention I have no control over dao classes, all I can change is the persistenceManager class. Please suggest any solution to modify the persistence mangager class.
1

I found the solution, see below

Updated

    @Repository
public class PersistenceManager
{
    /**
     * Key that stores the entity manager.
     */
    private static final String ENTITY_MANAGER_INSTANCE = "ENTITY_MANAGER_INSTANCE";
    /**
     * Persistence unit for eMyEd
     */
    private static final String PERSISTENCE_UNIT_EMYED = "application_openjpa";

    private static EntityManagerFactory emf;

    @PersistenceUnit(unitName=PERSISTENCE_UNIT_EMYED)
    public void setEntityManagerFactory(EntityManagerFactory emf) {
        PersistenceManager.emf = emf;
    }
    /**
     * Cleanup any entity managers created in the thread context.
     */
    public static void close()
    {
        EntityManager emInstance = getEntityManager();
        try
        {
            if (emInstance.isOpen())
            {
                emInstance.close();
            }
        }
        catch (Throwable t)
        {
            // ignore
        }
        finally
        {
            ThreadContext.instance.clear();
        }
    }

    /**
     * Returns the Entity manager associated with the given thread context. If
     * none available, one is created and set on the thread context and the same
     * is returned.
     *
     * @return
     */
    public static EntityManager getEntityManager()
    {
        EntityManager emInstance = (EntityManager) ThreadContext.instance
                .get(ENTITY_MANAGER_INSTANCE);
        if (emInstance == null)
        {
            emInstance = createEntityManager();
            emInstance.setFlushMode(FlushModeType.COMMIT);
            ThreadContext.instance.put(ENTITY_MANAGER_INSTANCE, emInstance);
        }
        try
        {
            // try to join the current active transaction
            emInstance.joinTransaction();
        }
        catch (TransactionRequiredException notSupportedEx)
        {
            // If there was no transaction to join. Ignore
        }
        catch (Throwable unknownEx)
        {
            // If there was no transaction to join. Ignore
        }
        return emInstance;
    }

    /**
     * Create a new entity manager.
     *
     * @return
     */
    private static EntityManager createEntityManager()
    {
        return emf.createEntityManager();
    }
}

1 Comment

Never ever hold an EntityManager in a static field. By definition, an EntityManager is not thread safe, so you'll run into issues with that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.