Search code examples
javaspringhibernateentitymanager

How to use Persistence EntityManager along with SessionFactory in spring 4 hibernate 5?


I was using sessionFactory by autowiring it in my DAOImpl files. Things were working fine until I started facing an issue of "Too many database connections" in certain DAO methods. After searching for possible reasons and solutions, I realized that I might have misconfigured my applicationContext.xml and the way I am using EntityManager is incorrect.

Below is my applicationContext.xml file content:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="  
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd  
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!-- Enables the Spring MVC @Controller programming model -->

    <!-- enables cors (cross origin request) for all urls -->
    <mvc:cors>
        <mvc:mapping path="/**" />
    </mvc:cors>

    <!-- <mvc:annotation-driven /> -->

     <mvc:annotation-driven>
        <mvc:message-converters>
            <!-- Use the HibernateAware mapper instead of the default -->
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="com.typjaipur.core.objectmapper.HibernateAwareObjectMapper" />
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <context:component-scan base-package="com.typjaipur" />

    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="classpath:messages" />
        <property name="defaultEncoding" value="UTF-8" />
    </bean>

    <bean id="propertyPlaceholder" class="com.typjaipur.core.config.EnvironmentPropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
                <value>classpath:core.properties</value>
                <value>classpath:mailer.properties</value>
            </list>
        </property> 
    </bean>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="com.typjaipur.model" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.default_batch_fetch_size">4</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.jdbc.batch_size">20</prop>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
            </props>
        </property>
    </bean>

    <bean id="hibernateTransactionManager"
        class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <tx:annotation-driven transaction-manager="hibernateTransactionManager" />

    <mvc:interceptors>
        <bean class="org.springframework.orm.hibernate5.support.OpenSessionInViewInterceptor">
            <property name="sessionFactory">
                <ref bean="sessionFactory" />
            </property>
        </bean>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <!-- excluded urls -->
            <mvc:exclude-mapping path="/" />
            <bean class="com.typjaipur.interceptor.ApiAuthenticationInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>


    <!-- Enables swgger ui -->
    <mvc:resources mapping="swagger-ui.html" location="classpath:/META-INF/resources/" />
    <mvc:resources mapping="/webjars/**"
        location="classpath:/META-INF/resources/webjars/" />

    <!-- Include a swagger configuration -->
    <bean name="/applicationSwaggerConfig" class="com.typjaipur.config.SwaggerConfig" />

    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- one of the properties available; the maximum file size in bytes -->
        <!-- <property name="maxUploadSize" value="100000"/> -->
    </bean>
</beans>

Below is DAOImpl code example of how I am using EntityManager which is working too but I feel its not correct maybe.

@Repository
public class BusinessDetailsDAOImpl extends BaseDAOImpl<BusinessDetails, Long> implements BusinessDetailsDAO {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public List<BusinessDetails> searchBusiness(BusinessSearchDTO businessSearchDTO, List<Long> businessIds) {

        EntityManager entityManager = sessionFactory.getCurrentSession().getEntityManagerFactory().createEntityManager();
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<BusinessDetails> query = criteriaBuilder.createQuery(BusinessDetails.class);
        Root<BusinessDetails> businessDetails = query.from(BusinessDetails.class);

        List<Predicate> predicates = new ArrayList<Predicate>();
...//rest of code

It is tough for me to avoid sessionFactory at this point as I have used it all over my project. Is there anyway through which I can configure my xml file to allow me to use EntityManager as well as SessionFactory together?

I saw several examples of configuring EntityManager but none of them has added any line in xml file related to SessionFactory. So I am confused in this.


Solution

  • Use LocalContainerEntityManagerFactoryBean to create the EntityManager instead of using LocalSessionFactoryBean, so you do not have call sessionFactory.getCurrentSession().getEntityManagerFactory().createEntityManager() to get the entityManager :

    1. In spring config file replace this current config with :
    <!-- Create a datasource -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
    
    </bean>
    
    <!-- Create an Hibernate to Jpa adapter -->
    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    
        <property name="generateDdl" value="true" />
        <property name="showSql" value="false" />
        <property name="database" value="MYSQL" />
    
    </bean>
    
    <!-- persistenceUnitManager is optional -->
    <bean id="persistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
    
        <property name="defaultDataSource" ref="dataSource"/>
    
    </bean>
    
    <!-- create entityManagerFactory -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    
        <property name="persistenceUnitManager" ref="persistenceUnitManager"/>
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
    
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            </props>
        </property>
    
    </bean>
    
    <!-- and create transactionManager -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    
    1. Use it like this :
    @Repository
    public class BusinessDetailsDAOImpl extends BaseDAOImpl<BusinessDetails, Long> implements BusinessDetailsDAO {
    
        @PersistenceContext
        protected EntityManager entityManager; 
    
        @Override
        public List<BusinessDetails> searchBusiness(BusinessSearchDTO businessSearchDTO, List<Long> businessIds) {
    
            // use directly entityManager instead of sessionFactory.getCurrentSession().getEntityManagerFactory().createEntityManager();
    
            CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    
            // ...
    
    }
    

    Edit : update your config as follow :

    <property name="jpaProperties">
        <props>
            <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    
            <!-- add this line -->
            <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
        </props>
    </property>