Announcement Announcement Module
Collapse
No announcement yet.
Example for using two databases w/Spring & JTA transaction manager? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Example for using two databases w/Spring & JTA transaction manager?

    Hi all,

    Question for the community. While I've had plenty of occasions to use a single database & single transaction manager with Spring, I've come across a situation where I need to use Spring's JtaTransactionManager with two database connections, and ensure that their transactions are committed atomically. The two databases are both Oracle, but they have completely different schemas & are on completely different servers. Further, I'm not using a traditional JEE container; this is in an OSGi environment, so I'll be using a third party standalone JTA implementation: candidates are Atomikos, JBossTS/Narayana, JOTM and Bitronix.

    If I define a single JtaTransactionManager (configured to manage my two Oracle database connections) and use JPA, how do I ensure that I get references to the "right" EntityManager when I need it? Can I write a single DAO that receives transactional injections of two EntityManager instances for the current distributed transaction?

    Assume any cross-database references are not persistent object references, but instead are just ids pointing to the object in the other database (for example, a org.example.Foo, stored in the first database, has a field barId that identifies an org.example.Bar in the second database, and vice versa).

    Thanks,
    Matthew

  • #2
    Hi Matthew,

    Recently I worked on an application that uses:
    Hibernate as ORM
    Oracle as database
    Two Hibernate session factories
    JTA for transaction management because of two hibernate session factories
    Glassfish Server

    Although your case is not same as of ours. But we are successfully able to develop our application with proper transaction management using JTA and is on production now.

    We have used JTATransactionManager class from spring to achieve tx management. We have also used OpenSessionInViewFilter from Spring for lazy-loading in web layer.

    If you are using Hibernate then you need to create two session factories and two data sources. Then you need to configure JTA like

    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTran sactionManager">
    <property name="allowCustomIsolationLevels" value="true"/>
    </bean>

    Your DAO classes need to be aware of session factory. Ideally One DAO is for entity and so one session factory. You need to start your transactions from business layer and inject your DAOs in business layer classes. Like:

    Inject EmployeeDAO and DepartmentDAO in CompanyService. EmployeeDAO will point to One session factory and DepartmentDAO with point to second session factory.

    Thanks

    Comment


    • #3
      Good to know, kpsingh. It sounds like the only difference is that where you're using Glassfish's JTA facilities, I'll be using a standalone JTA transaction manager (Atomikos/Naranaya/Bitronix/JOTM).

      Making your DAOs aware of the SessionFactory is easy enough, but who does the injection? Spring automatically or you manually?

      Comment


      • #4
        Hi Matthew,

        We are using Spring for dependency injection.

        Thanks
        Kamal

        Comment


        • #5
          Hi, We use JTA and JPA 2.0 with multiple persistence units in Spring. Our JTA is supplied from the Glassfish container. In the example below I show two different datasources/persistence units/entity manager factories where in fact we have quite a few more than that in practice. But the principles are the same.

          Each persistence unit is defined in the persistence.xml normally and they bind to JTA/XA datasources in the Glassfish environment using JNDI (of course you would get these through your own JTA manager, e.g. Atomikos, and some of the other configuration would be different too);

          Code:
            <persistence-unit name="onePU" transaction-type="JTA">
              <provider>org.hibernate.ejb.HibernatePersistence</provider>
              <jta-data-source>jdbc/oneDB</jta-data-source>
              <class>my.classes.here</class>
              <exclude-unlisted-classes />
              <properties>
                <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
                <property name="hibernate.default_schema " value="oneSchema" />
                <!-- you will need to definitely change this property below -->
                <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.SunONETransactionManagerLookup" />
                 <!-- and possibly many of these others -->
                <property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" />
                <property name="hibernate.cache.use_query_cache" value="true" />
                <property name="hibernate.cache.use_second_level_cache" value="true" />
                <property name="hibernate.generate_statistics" value="true" />
              </properties>
            </persistence-unit>
          
            <persistence-unit name="twoPU" transaction-type="JTA">
              <provider>org.hibernate.ejb.HibernatePersistence</provider>
              <jta-data-source>jdbc/twoDB</jta-data-source>
              <class>my.other.classes.here</class>
              <exclude-unlisted-classes />
              <properties>
                <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
                <property name="hibernate.default_schema " value="twoSchema" />
                <!-- other properties as above omitted for brevity -->
              </properties>
            </persistence-unit>
          The database mapping classes are annotated with the standard JPA annotations. Note the DB schema is specified in the persistence.xml above not in the database mapping classes! This allows simple integration test configurations using a single in-memory HSQLDB (doesn't support schemas) and so forth where a different persistence.xml is used that doesn't specify the schema or whatever is needed in the particular test configuration.

          The material point here is that nearly all the database configuration is now confined to either the persistence.xml or inside the Container (e.g. location/username/password/XA driver class etc). Meaning the rest of the application is now entirely independent of most of the database semantics.

          In the web.xml these persistence units are made available as JNDI resources and Persistence Units;

          Code:
            <persistence-unit-ref>
              <persistence-unit-ref-name>persistence/onePU</persistence-unit-ref-name>
              <persistence-unit-name>onePU</persistence-unit-name>
            </persistence-unit-ref>
          
            <persistence-unit-ref>
              <persistence-unit-ref-name>persistence/twoPU</persistence-unit-ref-name>
              <persistence-unit-name>twoPU</persistence-unit-name>
            </persistence-unit-ref>
          
            <persistence-context-ref>
              <persistence-context-ref-name>persistence/onePU</persistence-context-ref-name>
              <persistence-unit-name>onePU</persistence-unit-name>
            </persistence-context-ref>
          
            <persistence-context-ref>
              <persistence-context-ref-name>persistence/twoPU</persistence-context-ref-name>
              <persistence-unit-name>twoPU</persistence-unit-name>
            </persistence-context-ref>
          Now, the trick. In the Spring application context, this is my sum total of all the database configuration (!);

          Code:
            <tx:annotation-driven />
            <tx:jta-transaction-manager />
          
            <bean id="pabpp" class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
          
            <jee:jndi-lookup id="onePU" jndi-name="persistence/onePU" />
            <jee:jndi-lookup id="twoPU" jndi-name="persistence/twoPU" />
          The JNDI lookup of the persistence unit JNDI names means there is are two EntityManagerFactories in the app context called "onePU" and "twoPU". The PersistenceAnnotationBeanPostProcessor finds these and configures them such that on my DB service layer classes I am able inject the correct entity manager as follows;

          Code:
              @PersistenceContext(unitName = "onePU")
              private EntityManager em;
          Because the unitName in the @PersistenceContext matches the ID of the JNDI lookup entity manager factory Spring knows where to find the injected EntityManager. Although I'm fairly sure this facility is provided via the PersistenceAnnotationBeanPostProcessor in fact I'm not entirely sure whether that bean is necessary anymore (I used to inject the JNDI names directly into a property of the PersistenceAnnotationBeanPostProcessor and not have any external jndi-lookup, until I needed declarative access to the EntityManagerFactories elsewhere).

          Of course, I'm using Spring's @Transactional annotation on the methods of the DB service layer classes with appropriate isolation, propagation, rollback rules, and other configuration in the annotation as necessary;

          Code:
              @Transactional(propagation = Propagation.REQUIRES_NEW)
              public Message saveMessage(Message message) { ... }
          
              @Transactional(propagation = Propagation.MANDATORY)
              public List<Message> activateMessagesForCurrentConnection(ConnectionDetails connectionDetails) { ... }
          
              @Transactional(propagation = Propagation.REQUIRED, readOnly = true)
              public Message getMessageDetails(UUID messageId) { ... }
          Then in my web.xml I also put this filter;

          Code:
            <!-- ONE -->
            <filter>
              <filter-name>openEntityManagerInViewFilterEden</filter-name>
              <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
              <init-param>
                <param-name>entityManagerFactoryBeanName</param-name>
                <param-value>onePU</param-value>
              </init-param>
            </filter>
            <filter-mapping>
              <filter-name>openEntityManagerInViewFilterEden</filter-name>
              <url-pattern>/one/*</url-pattern>
            </filter-mapping>
            <!-- TWO -->
            <filter>
              <filter-name>openEntityManagerInViewFilterElec</filter-name>
              <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
              <init-param>
                <param-name>entityManagerFactoryBeanName</param-name>
                <param-value>twoPU</param-value>
              </init-param>
            </filter>
          <filter-mapping>
              <filter-name>openEntityManagerInViewFilterElec</filter-name>
              <url-pattern>/two/*</url-pattern>
            </filter-mapping>
          So now if a user opens anything on the path "/one/*" the "onePU" EntityManagerFactory is in scope, and likewise for "/two/*" (it is actually slightly more complex than that, there are paths where both must be in scope, and so forth).

          Although we had a difficult path getting this far (understanding the available options through the sparsity of the documentation around a configuration like this), I'm finding the overall solution now quite elegant.

          Hope this helps you.
          Last edited by scotartt; Oct 13th, 2011, 08:42 PM.

          Comment


          • #6
            Hi scotartt,

            This is a great example. The key point, I'd say, is that all of the JPA config is going in persistence.xml (instead of a Spring LocalContainerEntityManagerFactoryBean). I'll give it a go.

            Thanks,
            Matthew

            Comment


            • #7
              Hi,

              You can do something like below.....here I am using Autmikos TM ......hope this helps

              <bean id="AtomikosTransactionManager"
              class="com.atomikos.icatch.jta.UserTransactionMana ger"
              init-method="init"
              destroy-method="close" >
              <property name="forceShutdown" value="false" />
              </bean>


              <!-- Using Atomikos UserTransactionImp, needed to configure Spring -->
              <bean id="AtomikosUserTransaction"
              class="com.atomikos.icatch.jta.UserTransactionImp" >
              <property name="transactionTimeout" value="300" />
              </bean>


              <!-- Configure the Spring framework to use JTA transactions from Atomikos -->
              <bean id="JtaTransactionManager"
              class="org.springframework.transaction.jta.JtaTran sactionManager">
              <property name="transactionManager" ref="AtomikosTransactionManager" />
              <property name="userTransaction" ref="AtomikosUserTransaction" />
              </bean>

              Thanks,
              Shekhar

              Comment

              Working...
              X