Saturday 22 June 2013

Spring RESOURCE_LOCAL and JTA configurations

Spring RESOURCE_LOCAL(Local Transactions) and JTA(Global Transactions) configurations

So often people tend to make many mistakes with RESOURCE_LOCAL and JTA configurations, Without knowing the actual usage of tags, developers often mix these two configuration related tags. By doing so, spring instantiate unwanted beans in memory. So, I thought this blow would helps to developers to fix or create a proper RESOURCE_LOCAL and JTA xml configurations. I will also explain how to create proper JTA production configuration. Yes, JTA can be configured in different ways. But for production, Its better to handle the connections by the application container itself. So before start creating these configurations one has to have a complete knowledge of the xml tags used in these configurations.I would suggest you to read and understand all the topics discussed in http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

Global Transactions. Global transactions have a significant downside, in that code needs to use JTA, and JTA is a cumbersome API to use (partly due to its exception model). Furthermore, a JTAUserTransaction normally needs to be sourced from JNDI: meaning that we need to use both JNDIand JTA to use JTA. Obviously all use of global transactions limits the reusability of application code, as JTA is normally only available in an application server environment. Previously, the preferred way to use global transactions was via EJB CMT (Container Managed Transaction): CMT is a form of declarative transaction management (as distinguished from programmatic transaction management). EJB CMT removes the need for transaction-related JNDI lookups - although of course the use of EJB itself necessitates the use of JNDI. It removes most of the need (although not entirely) to write Java code to control transactions. The significant downside is that CMT is tied to JTA and an application server environment. Also, it is only available if one chooses to implement business logic in EJBs, or at least behind a transactional EJB facade. The negatives around EJB in general are so great that this is not an attractive proposition, especially in the face of compelling alternatives for declarative transaction management.
Local Transactions. Local transactions may be easier to use, but have significant disadvantages: they cannot work across multiple transactional resources. For example, code that manages transactions using a JDBC connection cannot run within a global JTA transaction. Another downside is that local transactions tend to be invasive to the programming model.
Spring resolves these problems. It enables application developers to use a consistent programming model in any environment. You write your code once, and it can benefit from different transaction management strategies in different environments. The Spring Framework provides both declarative and programmatic transaction management. Declarative transaction management is preferred by most users, and is recommended in most cases.
With programmatic transaction management, developers work with the Spring Framework transaction abstraction, which can run over any underlying transaction infrastructure. With the preferred declarative model, developers typically write little or no code related to transaction management, and hence don't depend on the Spring Framework's transaction API (or indeed on any other transaction API).

We can use Hibernate/Eclipselink/Toplink,etc as the unerlying ORM technology, since I am familier with JPA 2 and its also the default implementation of glassfish I would always prefer to go with eclipselink for my web applications. Configured with Spring 3.1.2 release and mysql database and tested in glassfish 3.1.2.2 server.


Is an application server needed for transaction management?
The Spring Framework's transaction management support significantly changes traditional thinking as to when a J2EE application requires an application server.
In particular, you don't need an application server just to have declarative transactions via EJB. In fact, even if you have an application server with powerful JTA capabilities, you may well decide that the Spring Framework's declarative transactions offer more power and a much more productive programming model than EJB CMT.
Typically you need an application server's JTA capability only if you need to enlist multiple transactional resources, and for many applications being able to handle transactions across multiple resources isn't a requirement. For example, many high-end applications use a single, highly scalable database (such as Oracle 9i RAC). Standalone transaction managers such as Atomikos Transactions and JOTM are other options. (Of course you may need other application server capabilities such as JMS and JCA.)
The most important point is that with the Spring Framework you can choose when to scale your application up to a full-blown application server. Gone are the days when the only alternative to using EJB CMT or JTA was to write code using local transactions such as those on JDBC connections, and face a hefty rework if you ever needed that code to run within global, container-managed transactions. With the Spring Framework, only configuration needs to change so that your code doesn't have to.


Below you can see the JTA configuration with entityManagerFactory bean created from LocalContainerEntityManagerFactoryBean class


<?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:mvc="http://www.springframework.org/schema/mvc"
        xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
                http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd" default-autowire="byName">


<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="xxxx"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter" >
                <property name="showSql" value="false" />
                <property name="generateDdl" value="false" />
                <property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform" />
            </bean>
        </property>
        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
        </property>
    </bean>               
        
        <tx:annotation-driven /> 
        <tx:jta-transaction-manager/> 
        
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />


</beans>


JTA configuration with entityManagerFactory bean created from JNDI


<?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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
                http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd" default-autowire="byName">    
    
    <jee:jndi-lookup id="entityManagerFactory" jndi-name="jdbc/xxxx"/>
    <tx:annotation-driven /> 
    <tx:jta-transaction-manager/> 
        
    <bean
        class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>

If we are using jndi lookup in spring configuration file. Its necessary to place persistence-unit-ref in web.xml

web.xml
<persistence-unit-ref>
        <persistence-unit-ref-name>jdbc/xxxx</persistence-unit-ref-name>
        <persistence-unit-name>xxxx</persistence-unit-name>
    </persistence-unit-ref>

persistence.xml ( JTA )

<?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="xxxx" transaction-type="JTA">
    <jta-data-source>jdbc/xxxx</jta-data-source>
    <properties>
      <property name="eclipselink.jpa.uppercase-column-names" value="true"/>      
      <property name="eclipselink.cache.shared.default" value="false"/> 
      <property name="javax.persistence.lock.timeout" value="1000"/>     
      <property name="eclipselink.logging.level" value="SEVERE" />      
      <property name="eclipselink.target-server" value="SunAS9"/>
    </properties>
  </persistence-unit>
</persistence>


Below you can see the RESOURCE_LOCAL configuration with entityManagerFactory bean created from LocalContainerEntityManagerFactoryBean class

<?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:mvc="http://www.springframework.org/schema/mvc"
       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-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd" default-autowire="byName">

    
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">        
        <property name="persistenceUnitName" value="xxxx"/>
        <property name="persistenceXmlLocation" value="classpath:META-INF/test-persistence.xml"/>
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect"/>
        </property>
        <property name="jpaPropertyMap">
            <props>
                <prop key="eclipselink.weaving">false</prop>
            </props>
        </property>

        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver">
            </bean>
        </property>        
    </bean>

    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
        <property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform" />
        <property name="generateDdl" value="false"/>
        <property name="showSql" value="true"/>
    </bean>

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

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:annotation-driven />

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

</beans>


persistence.xml ( RESOURCE_LOCAL )


<?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="xxxx" transaction-type="RESOURCE_LOCAL">
    <class>xxxx</class>    
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties/>
  </persistence-unit>
</persistence>

To create web applications I always use maven archetype that helps to automate the build process, dependency management, SCM, etc. If you find any difficulty in getting the required dependencies please use the below pom.xml file.
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <jetty.version>6.1.4</jetty.version>
        <spring.version>3.1.2.RELEASE</spring.version>
        <spring.security.version>3.1.0.RELEASE</spring.security.version>
        <slf4j.version>1.5.10</slf4j.version>
        <java.version>1.6</java.version>
        <junit.version>4.10</junit.version>
        <mysql.connector.version>5.1.16</mysql.connector.version>
        <netbeans.hint.deploy.server>gfv3ee6</netbeans.hint.deploy.server>
    </properties>

    <dependencies>
     
         <dependency>
            <groupId>org.glassfish.extras</groupId>
            <artifactId>glassfish-embedded-all</artifactId>
            <version>3.2-b06</version>
            <scope>provided</scope>
        </dependency>  
               
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-core-asl</artifactId>
            <version>0.9.8</version>
        </dependency>  
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>3.5</version>
        </dependency>
        <dependency>
            <groupId>org.primefaces.themes</groupId>
            <artifactId>all-themes</artifactId>
            <version>1.0.9</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>


        <!-- Spring Dependencies -->

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <type>jar</type>
            <scope>test</scope>
        </dependency>

        <!-- spring security -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${spring.security.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- DB Dependencies -->
        <!-- MySQL Connector -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.connector.version}</version>
            <type>jar</type>
            <scope>provided</scope>
        </dependency>
        <!-- Test Dependencies -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jettison</groupId>
            <artifactId>jettison</artifactId>
            <version>1.3.3</version>
        </dependency>
       
        <!-- logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
            <exclusions>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
            </exclusions>
            <scope>compile</scope>
        </dependency>
<dependency>
            <groupId>log4j</groupId>
            <artifactId>apache-log4j-extras</artifactId>
            <version>1.1</version>
        </dependency>

    </dependencies>


I would recommend JTA configuration for production environment and RESOURCE_LOCAL for unit testing. With RESOURCE_LOCAL we have to take care of connection pooling and any change in connection pooling needs an application redepolyment. But with JTA, your application server provides connection pooling, transaction management and at run time we can change the connection pool settings without the need for application redeployment.

No comments:

Post a Comment