Spring and JPA with two data sources (with annotations)

A few days I recevied a comment from my friend @sock_osg (you can follow on twitter) he recomends me to rewrite my previous post with annotations.

And well here is it. But the are more few things to comment about it, for example in the previous post there are not a Transactional capabilities between DB’s because I’m not using JTA transaction type.

Now for this example I write the code to support transactions between multple data bases with different data sources, like I read on stack overflow:

if you find yourself with multiple entity managers, with corresponding tx managers, then you should consider using a single JtaTransactionManager instead. The entity managers should be able to participate in JTA transactions, and this will give you full transactionality across both entity managers, without having to worry about which entity manager you’re in at any one time.

You can download all code here, it’s hosted on my Github account, pull the code from the branch named “annotations”.

Let’s review the most important files, frst the DAO classes: Seguir leyendo

Anuncios

Spring and JPA with two data sources

JPA it’s the most used standar in java to manage the persistence and I ever want to write about this topic.

In this entry I want to share how configure a java project to use JPA with spring with two data sources, you can view all source of this project on my github repository.

Most of the configuration is in spring, in the application context you need declare:

  • 2 Data Sources
  • 2 Entity Manager Factories
  • 2 Transaction Manager

This is explained by self:

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

    <context:property-placeholder location="classpath:app-props.properties" />

    <context:component-scan base-package="org.oz" />
    <context:annotation-config/>

    <bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
        <property name="persistenceXmlLocations">
            <list>
                <value>classpath:META-INF/persistence.xml</value>
            </list>
        </property>
        <property name="dataSources">
            <map>
                <entry key="localDataSource" value-ref="dataSource1"/>
                <entry key="remoteDataSource" value-ref="dataSource2"/>
            </map>
        </property>

        <!-- if no datasource is specified, use this one -->
        <property name="defaultDataSource" ref="dataSource1"/>
        <property name="defaultPersistenceUnitName" value="unit1"/>

    </bean>

    <bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true" primary="true"
          p:driverClassName="${jdbc1.driver}"
          p:url="${jdbc1.url}"
          p:username="${jdbc1.user}"
          p:password="${jdbc1.pass}"
    />

    <bean id="entityManagerFactory1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
          p:persistenceXmlLocation="classpath:META-INF/persistence.xml"
          p:persistenceUnitName="unit1"
          p:dataSource-ref="dataSource1"
          p:packagesToScan="org.oz.persistence.dao.db1"
          lazy-init="true"/>

    <bean id="transactionManager1" class="org.springframework.orm.jpa.JpaTransactionManager"
          p:entityManagerFactory-ref="entityManagerFactory1"
          lazy-init="true"/>

    <bean id="dataSource2" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true"
          p:driverClassName="${jdbc2.driver}"
          p:url="${jdbc2.url}"
          p:username="${jdbc2.user}"
          p:password="${jdbc2.pass}"
    />

    <bean id="entityManagerFactory2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
          p:persistenceXmlLocation="classpath:META-INF/persistence.xml"
          p:persistenceUnitName="unit2"
          p:dataSource-ref="dataSource2"
          p:packagesToScan="org.oz.persistence.dao.db2"
          lazy-init="true"/>

    <bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager"
          p:entityManagerFactory-ref="entityManagerFactory2"
          lazy-init="true"/>

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

    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

    <bean id="customerDao" class="org.oz.persistence.dao.db1.CustomerDao"/>
    <bean id="productDao" class="org.oz.persistence.dao.db2.ProductDao"/>

</beans>

 

Now in peresistence.xml we going to declare two persistence units:

<?xml version="1.0" encoding="UTF-8"?>
<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="unit1" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <mapping-file>META-INF/native-querys.xml</mapping-file>
        <class>org.oz.persistence.dao.db1.model.Customer</class>

        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
            <property name="hibernate.archive.autodetection" value="class,hbm"/>
            <property name="useUnicode" value="true"/>
            <property name="characterSetResults" value="UTF8"/>
            <property name="characterEncoding" value="UTF8"/>
            <property name="hibernate.format_sql" value="false"/>
            <property name="hibernate.use_sql_comments" value="false"/>
            <property name="hibernate.hbm2ddl.keywords" value="auto-quote"/>
            <property name="hibernate.bytecode.use_reflection_optimizer" value="true"/>
            <property name="hibernate.connection.useUnicode" value="true"/>
            <property name="hibernate.connection.characterEncoding" value="UTF8"/>
            <property name="hibernate.connection.charSet" value="UTF8"/>
            <property name="hibernate.connection.characterSetResults" value="UTF8"/>

            <property name="hibernate.default_schema" value="BASEA"/>
        </properties>

    </persistence-unit>

    <persistence-unit name="unit2" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <mapping-file>META-INF/native-querys.xml</mapping-file>
        <class>org.oz.persistence.dao.db2.model.Product</class>

        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
            <property name="hibernate.archive.autodetection" value="class,hbm"/>
            <property name="useUnicode" value="true"/>
            <property name="characterSetResults" value="UTF8"/>
            <property name="characterEncoding" value="UTF8"/>
            <property name="hibernate.format_sql" value="false"/>
            <property name="hibernate.use_sql_comments" value="false"/>
            <property name="hibernate.hbm2ddl.keywords" value="auto-quote"/>
            <property name="hibernate.bytecode.use_reflection_optimizer" value="true"/>
            <property name="hibernate.connection.useUnicode" value="true"/>
            <property name="hibernate.connection.characterEncoding" value="UTF8"/>
            <property name="hibernate.connection.charSet" value="UTF8"/>
            <property name="hibernate.connection.characterSetResults" value="UTF8"/>

            <property name="hibernate.default_schema" value="BASEB"/>
        </properties>

    </persistence-unit>
</persistence>

Then only we’re need to do is set the reference of the persistence unit in every DAO to inject the EntityManager:

package org.oz.persistence.dao.db1;

import javax.persistence.*;
import java.util.Collection;

/**
 * Created by <a href="https://twitter.com/jaehoox">jaehoo</a> on 16/03/2018
 */
public class CustomerDao {

    public static final String SEL_TABLES="select.tablesh2";

    @PersistenceContext(unitName = "unit1" , type = PersistenceContextType.TRANSACTION)
    private EntityManager em;

    public Collection loadCustomers() {
        Query query = em.createQuery("FROM Customer");
        return query.getResultList();

    }

    public Collection getTables(){
        return em.createNamedQuery(SEL_TABLES).getResultList();
    }

}

package org.oz.persistence.dao.db2;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.Query;
import java.util.Collection;

/**
 * Created by <a href="https://twitter.com/jaehoox">jaehoo</a> on 16/03/2018
 */
public class ProductDao {

    public static final String SEL_TABLES="select.tablesh2";

    @PersistenceContext(unitName = "unit2", type = PersistenceContextType.TRANSACTION, name = "unit2")
    private EntityManager em;

    public Collection loadProducts() {
        Query query = em.createQuery("FROM Product");
        return query.getResultList();

    }

    public Collection getTables(){
        return em.createNamedQuery(SEL_TABLES).getResultList();
    }

}

The interesting point of this is in the use of the transaction manager, lets take a look on test class:

package org.oz.persistence.dao.db1;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.oz.persistence.dao.db1.model.Customer;
import org.oz.persistence.dao.db2.ProductDao;
import org.oz.persistence.dao.db2.model.Product;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import java.util.ArrayList;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:app-ctx-test.xml"})
@TransactionConfiguration
@Slf4j
public class CustomerDaoTest {

    @Resource(name = "customerDao")
    private CustomerDao customerDao;

    @Resource(name = "productDao")
    private ProductDao productDao;

    @Test
    @Transactional("transactionManager1")
    public void loadCustomers() throws Exception {

        List customers = (List) customerDao.loadCustomers();

        log.info("customers:{}",customers.size());

        for(Customer c : customers){
            log.info("{}",c);
        }

        List tables = (List) customerDao.getTables();

        log.info("tables:{}",tables.size());
        for(Object c : tables){
            log.info("{}",c);
        }

    }

    @Test
    @Transactional("transactionManager2")
    public void loadProducts() throws Exception {

        List products = (List) productDao.loadProducts();

        log.info("products:{}",products.size());
        log.info("{}",products.get(0));

        List tables = (List) productDao.getTables();

        log.info("tables:{}",tables.size());
        for(Object c : tables){
            log.info("{}",c);
        }

    }

    @Test
    public void queryngTwoSoruces() throws Exception {

        log.info("getting data from two DS in one method");
        List tables = new ArrayList();

        tables.addAll(productDao.getTables());
        tables.addAll(customerDao.getTables());

        log.info("tables:{}",tables.size());
        for(Object c : tables){
            log.info("{}",c);
        }

    }

}

The first lines load the application context and inject DAO classes with the Entity Manager.

The first and the second method get all records of the customer and Product table and get all data base table names, each of one is using their corresponding transaction manager (as you can see on @Transactional annotation), until here everything is clear you can querying data from each data base.

But what happens in the third method??

Interesting rigth? and the anwser is… there is not a Transaction implicit, yeah so simple! it works but I don’t have transactional capabilities, that means that I need to manage each transaction in separated ways.

But I read this in stackoverflow:

The javadoc for JpaTransactionManager has some advice on this:

“This transaction manager is appropriate for applications that use a single JPA EntityManagerFactory for transactional data access. JTA (usually through 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.”

In other words, if you find yourself with multiple entity managers, with corresponding tx managers, then you should consider using a single JtaTransactionManager instead. The entity managers should be able to participate in JTA transactions, and this will give you full transactionality across both entity managers, without having to worry about which entity manager you’re in at any one time.

Of course, JtaTransactionManager does require a full JTA-supporting application server, rather than a vanilla servlet engine like Tomcat.

Maybe one day I’ll write about how to do this with the server container but now this is enough.

Cheers

Hibernate / JPA Clear Cache

Manejar persistencia puede ser un poco complejo si no se conoce el funcionamientos de los caches, generalmente cuando tienes configurado el framework para utilizarlos tienes que actualizar los datos obligatoriamente desde el código de tu aplicación.

Si por alguna razón los datos son actualizados en la base de datos y el framework no tiene conocimiento de estos cambios, en el cache vas a tener la información vieja e inconsistente.

Esto NO es un error, depende de la forma en como configuras tus querys y de la arquitectura de tu aplicación.

Para poder indicarle al framework que se ejecute el refresh de forma manual al cache de Nivel 1 puedes hacerlo mediente el siguiente código:

  • Para JPA obtienes la referencia al EntityManager o EntityManagerFactory y ejecutas el método clear().
  • Para Hibernate debes utilizar el método evict().

Es muy importante conocer en que puntos de tu aplicación debes utilizar el cache ya que puede ser integral en todas tus consultas, por entidades o por querys.

Lo recomendable es aplicarlo en consultas recurrentes o de catálogos.

Referencias:

Saludos!

EJB 3 Overview


Recientenmente he tenido que analizar un proyecto web que se encuentra divido en dos partes (backend y frontend), el entregable es un ear con un jar y un war.

Todo el backend esta constriudo con EJB’s y aunque siempre he escuchado pestes de esta tecnología nunca le he dado un vistazo. Así que comencé por leer algunos artículos y en realidad me parece que los EJB’s son una buena iniciativa en Java para simplificar el desarrollo y adoptar mejores prácticas. Particularmente me ha encantado este:

http://refcardz.dzone.com/refcardz/dependency-injection-in-ejb3 Seguir leyendo