Thursday 18 September 2014

Multiple DataSources with Hibernate - Approach 2

The later versions spring2.0.1 support configure multiple data sources, different data sources and can be loaded dynamically at run time. Can be achieved through inheritance AbstractRoutingDataSource dynamic conversion of multiple data sources. Do project is the need to access the data source, each data source table structure is the same, it requires changes in the data source is transparent for coders, that is to say the same SQL statement that operate in different circumstances The database is not the same. The specific configuration is as follows: You first need to write a static control key class:

      package cn.com.xinli.ccp.dynamicds; 
  
    public   class DataSourceMap { 
     public   static   final String Admin = "Admin"; 
     public   static   final String Yxh = "Yxh"; 
  } 

This class when in use as a data source flag. Second, the establishment of a class to get and set the context:

      public   class CustomerContextHolder { 
         private   static   final ThreadLocal contextHolder = 
                                new ThreadLocal (); 

      public   static   void setCustomerType (String customerType) { 
            contextHolder.set (customerType); 
       } 
      
      public   static String getCustomerType () { 
        return  (String) contextHolder.get (); 
       } 

      public   static   void clearCustomerType () { 
          contextHolder.remove (); 
        }    
    }

This is mainly responsible for setting the context and the context. Third, the establishment of a dynamic data source class, class must inherit AbstractRoutingDataSource

Multiple DataSources with Hibernate - Approach1

The demo application used in the posts mentioned presents how to use a REST API to execute CRUD operations against a back-end db, delivering podcasts. For the demo’s sake I’ll say a client of the REST API also needs resources(podcasts) from a “legacy” system – she needs to combine them with the ones from the actual system and present them to their users. For that I will implement two new read operations that will GET the podcast(s) from the “legacy” system. The new REST facade layer – PodcastLegacyRestService – that delivers “legacy” resources will use the same data access layer to highlight the use of multiple data sources. Now let’s see how to configure and code multiple data sources with Spring and JPA: The first thing I did was to modify the persistence.xml file by adding a new persistence unit that will correspond to the “legacy” entityManager, managing the new “legacy” data source:

persistence.xml

<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
                http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
   <persistence-unit name="demoRestPersistence">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
   </persistence-unit>
 
   <persistence-unit name="demoRestPersistenceLegacy">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
   </persistence-unit>   
</persistence>

Note: The persistence unit defines a set of all entity classes that are managed by EntityManager instances in an application. This set of entity classes represents the data contained within a single data store. Because I am using Spring you can see the configuration of the persistence unit in the persistence.xml is very lean. The actual configuration of the entity managers takes places in the Spring’s application context of the application. See the next section for the details.

In the Spring application context I just added new beans for the entity manager, transaction manger and datasource:

<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"    
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.codingpedia.demo.rest.*" />
 
    <!-- ************ JPA configuration *********** -->
    <tx:annotation-driven transaction-manager="transactionManager" />  
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <bean id="transactionManagerLegacy" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactoryLegacy" />
    </bean>    
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation" value="classpath:config/persistence-demo.xml" />
        <property name="persistenceUnitName" value="demoRestPersistence" />        
        <property name="dataSource" ref="restDemoDS" />
        <property name="packagesToScan" value="org.codingpedia.demo.*" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
            </bean>
        </property>
    </bean>     
    <bean id="entityManagerFactoryLegacy" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation" value="classpath:config/persistence-demo.xml" />
        <property name="persistenceUnitName" value="demoRestPersistenceLegacy" />
        <property name="dataSource" ref="restDemoLegacyDS" />
        <property name="packagesToScan" value="org.codingpedia.demo.*" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
            </bean>
        </property>
    </bean>  

<bean id="podcastDao" class="org.codingpedia.demo.rest.dao.impl.PodcastDaoJPA2Impl"/>    
    <bean id="podcastRestService" class="org.codingpedia.demo.rest.service.PodcastRestService" />
    <bean id="podcastLegacyRestService" class="org.codingpedia.demo.rest.service.PodcastLegacyRestService" />
 
    <bean id="restDemoDS" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
        <property name="jndiName" value="java:comp/env/jdbc/restDemoDB" />
        <property name="resourceRef" value="true" />        
    </bean>
    <bean id="restDemoLegacyDS" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
        <property name="jndiName" value="java:comp/env/jdbc/restDemoLegacyDB" />
        <property name="resourceRef" value="true" />        
    </bean>    
</beans>
      

Note the persistenceUnitName property of the entityManagerFactory is pointing to the corresponding persistence unit. Here’s a quick recap of the main configured beans:

  • entityManagerFactoryLegacy (org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean) is a org.springframework.beans.factory.FactoryBean that creates a JPA javax.persistence.EntityManagerFactory according to JPA’s standard container bootstrap contract. This is the most powerful way to set up a shared JPA EntityManagerFactory in a Spring application context; the EntityManagerFactory can then be passed to JPA-based DAOs via dependency injection. Note that switching to a JNDI lookup or to a LocalEntityManagerFactoryBean definition is just a matter of configuration!
    As with LocalEntityManagerFactoryBean, configuration settings are usually read in from a META-INF/persistence.xml config file, residing in the class path, according to the general JPA configuration contract. However, this FactoryBean is more flexible in that you can override the location of the persistence.xml file (as here the case), specify the JDBC DataSources to link to, etc. Furthermore, it allows for pluggable class instrumentation through Spring’s org.springframework.instrument.classloading.LoadTimeWeaver abstraction, instead of being tied to a special VM agent specified on JVM startup. 
  • transactionManagerLegacy (org.springframework.orm.jpa.JpaTransactionManager) is a org.springframework.transaction.PlatformTransactionManager implementation for a single JPA javax.persistence.EntityManagerFactory. Binds a JPA EntityManager from the specified factory to the thread, potentially allowing for one thread-bound EntityManager per factory. SharedEntityManagerCreator and JpaTemplate are aware of thread-bound entity managers and participate in such transactions automatically. Using either is required for JPA access code supporting this transaction management mechanism. This transaction manager is appropriate for applications that use a single JPA EntityManagerFactory for transactional data access. JTA (usually through org.springframework.transaction.jta.JtaTransactionManager) is necessary for accessing multiple transactional resources within the same transaction. Note that you need to configure your JPA provider accordingly in order to make it participate in JTA transactions.
As mentioned before to highlight the multiple data source configuration in code I extended the DAO layer class with methods to access the “legacy” system:

package org.codingpedia.demo.rest.dao.impl;
 
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
 
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;
 
import org.codingpedia.demo.rest.dao.PodcastDao;
import org.codingpedia.demo.rest.entities.Podcast;
 
public class PodcastDaoJPA2Impl implements PodcastDao {
 
    @PersistenceContext(unitName="demoRestPersistence")
    private EntityManager entityManager;
 
    @PersistenceContext(unitName="demoRestPersistenceLegacy")
    private EntityManager entityManagerLegacy;
 
    public List<Podcast> getPodcasts() {
 
        String qlString = "SELECT p FROM Podcast p";
        TypedQuery<Podcast> query = entityManager.createQuery(
                      qlString, Podcast.class);        
 
        return query.getResultList();
    }
    /* .............................*/
    public List<Podcast> getLegacyPodcasts() {
 
        String qlString = "SELECT p FROM Podcast p";
        TypedQuery<Podcast> query = entityManagerLegacy.createQuery(
                            qlString, Podcast.class);        
 
        return query.getResultList();
    }
 
}

Saturday 13 September 2014

Hibernate Interceptors


Hibernate Interceptors

The Interceptor interface provides callbacks from the session to the application, allowing the application to inspect and/or manipulate properties of a persistent object before it is saved, updated, deleted or loaded. One possible use for this is to track auditing information. For example, the following Interceptor automatically sets the createTimestamp when an Auditable is created and updates the lastUpdateTimestamp property when an Auditable is updated. You can either implement Interceptor directly or extend EmptyInterceptor.

    
package org.hibernate.test;

import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
public class AuditInterceptor extends EmptyInterceptor {
    private int updates;
    private int creates;
    private int loads;
    public void onDelete(Object entity,
        Serializable id,
        Object[] state,
        String[] propertyNames,
        Type[] types) {
        // do nothing
    }
    public boolean onFlushDirty(Object entity,
        Serializable id,
        Object[] currentState,
        Object[] previousState,
        String[] propertyNames,
        Type[] types) {
        if (entity instanceof Auditable) {
            updates++;
            for (int i = 0; i < propertyNames.length; i++) {
                if ("lastUpdateTimestamp".equals(propertyNames[i])) {
                    currentState[i] = new Date();
                    return true;
                }
            }
        }
        return false;
    }
    public boolean onLoad(Object entity,
        Serializable id,
        Object[] state,
        String[] propertyNames,
        Type[] types) {
        if (entity instanceof Auditable) {
            loads++;
        }
        return false;
    }
    public boolean onSave(Object entity,
        Serializable id,
        Object[] state,
        String[] propertyNames,
        Type[] types) {
        if (entity instanceof Auditable) {
            creates++;
            for (int i = 0; i < propertyNames.length; i++) {
                if ("createTimestamp".equals(propertyNames[i])) {
                    state[i] = new Date();
                    return true;
                }
            }
        }
        return false;
    }
    public void afterTransactionCompletion(Transaction tx) {
        if (tx.wasCommitted()) {
            System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads);
        }
        updates = 0;
        creates = 0;
        loads = 0;
    }
}

And here's an explanation of the single callback methods:

Method       Explanation
findDirty      This method is be called when the flush() method is called on a Session object.
instantiate     This method is called when a persisted class is instantiated.
isUnsaved      This method is called when an object is passed to the saveOrUpdate() method/
onDelete      This method is called before an object is deleted.
onFlushDirty     This method is called when Hibernate detects that an object is dirty (ie. have been    
             changed) during a flush i.e. update operation.
onLoad       This method is called before an object is initialized.
onSave       This method is called before an object is saved.
postFlush      This method is called after a flush has occurred and an object has been updated in memory.
preFlush      This method is called before a flush