Spring, JPA, Hibernate, and JpaTransactionManager configuration
the original configuration with a single datasource, Hibernate as the JPA provider, and the JpaTransactionManager was configured as outlined in the Spring reference manual and various posts on the web. please note that the datasource configuration within the persistence.xml JPA configuration file can be moved to the Spring configuration file as shown in this getting started with JPA in Spring 2.0 post.
persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence 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_1_0.xsd" version="1.0"> <!-- transaction type set to RESOURCE_LOCAL, no JTA support --> <persistence-unit name="DefaultPersistenceUnit" transaction-type="RESOURCE_LOCAL"> <properties> <property name="hibernate.archive.autodetection" value="class, hbm"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver"/> <property name="hibernate.connection.url" value="jdbc:oracle:thin:@HOST:PORT:SID"/> <property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/> <property name="hibernate.connection.username" value="USERNAME"/> <property name="hibernate.connection.password" value="PASSWORD"/> </properties> </persistence-unit> </persistence>
spring-context.xml
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <!-- enables interpretation of the @Required annotation to ensure that dependency injection actually occures --> <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/> <!-- enables interpretation of the @PersistenceUnit/@PersistenceContext annotations providing convenient access to EntityManagerFactory/EntityManager --> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/> <!-- uses the persistence unit defined in the META-INF/persistence.xml JPA configuration file --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"/> <!-- transaction manager for use with a single JPA EntityManagerFactory for transactional data access to a single datasource --> <bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <!-- enables interpretation of the @Transactional annotation for declerative transaction managment using the specified JpaTransactionManager --> <tx:annotation-driven transaction-manager="jpaTransactionManager" proxy-target-class="false"/> <!-- enables interpretation of the @Configurable annotation for domain object dependency injection --> <aop:spring-configured/> </beans>
Spring, JPA, Hibernate, JtaTransactionManager, and JOTM configuration
defining the XA datasources for use with the JOTM transaction manager is well documented. merely the fact that the JtaTransactionManager autodetects most JTA transaction managers but not the standalone JOTM transaction manager as documented in the JtaTransactionManager Javadoc was a bit tricky. a static accessor method is required which is the reason why the JotmFactoryBean has to be used. the object returned by the JotmFactoryBean implements both the UserTransaction and TransactionManager interface which is detected by the JtaTransactionManager implementation. it is, therefore, sufficient to supply the JotmFactoryBean merely to the userTransaction property of the JtaTransactionManager and not necessary to specify another reference to the transactionManager property.
once i had the XA datasources and the JtaTransactionManager in place the tricky part was to replace the JpaTransactionManager with the newly defined JtaTransactionManager and to configure the Hibernate JPA provider accordingly to make it participate in JTA transactions. Spring offers two ways to setup the JPA EntityManagerFactory. i switched the configuration to the more powerful LocalContainerEntityManagerFactoryBean and moved the properties previously defined in the persistence.xml JPA configuration file to the Spring configuration. furthermore, i replaced the datasource definition with a reference to the XA datasource and implemented a custom PersistenceUnitPostProcessor to be able to specify the JTA capable datasource directly within the Spring configuration file. in addition, setting the jpaPropertyMap property of the LocalContainerEntityManagerFactoryBean correctly to tell Hibernate to use the JOTM JTA transaction manager is necessary.
please note that a number of optional configuration properties can be supplied to Hibernate via the jpaPropertyMap. make sure to read and understand the sections on transaction strategy configuration, current session context management with JTA, and contextual sessions and set the corresponding configuration properties according to your needs.
in our current scenario we require JPA access to one of the XA datasources while JDBC is used to access the other datasource. that's the reason why no entity manager factory is defined for the second datasource. it should, however, be possible to adapt the configuration shown below and configure entity manager factories for both XA datasources.
persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence 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_1_0.xsd" version="1.0"> <!-- transaction type set to JTA, transaction suspension and XA supported --> <persistence-unit name="SpringConfiguredPersistenceUnit" transaction-type="JTA"/> </persistence>
spring-context.xml
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <!-- enables interpretation of the @Required annotation to ensure that dependency injection actually occures --> <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/> <!-- enables interpretation of the @PersistenceUnit/@PersistenceContext annotations providing convenient access to EntityManagerFactory/EntityManager --> <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/> <!-- first XA data source --> <bean id="bdbInnerDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource"> <property name="transactionManager" ref="jotm"/> <property name="driverName" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@HOST:PORT:SID"/> <property name="user" value="USERNAME"/> <property name="password" value="PASSWORD"/> </bean> <!-- first XA data source pool --> <bean id="bdbDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"> <property name="transactionManager" ref="jotm"/> <property name="dataSource" ref="bdbInnerDataSource"/> <property name="user" value="USERNAME"/> <property name="password" value="PASSWORD"/> <property name="maxSize" value="4"/> </bean> <!-- second XA data source --> <bean id="bpeInnerDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource"> <property name="transactionManager" ref="jotm"/> <property name="driverName" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@HOST:PORT:SID"/> <property name="user" value="USERNAME"/> <property name="password" value="PASSWORD"/> </bean> <!-- second XA data source pool --> <bean id="bpeDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"> <property name="transactionManager" ref="jotm"/> <property name="dataSource" ref="bpeInnerDataSource"/> <property name="user" value="USERNAME"/> <property name="password" value="PASSWORD"/> <property name="maxSize" value="4"/> </bean> <!-- required cos the standalone JOTM transaction manager is not autodetected by the JtaTransactionManager cos it requires a static accessor method --> <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/> <!-- supplying the JotmFactoryBean merely to the userTransaction property cos JtaTransactionManager autodetects that the object returned by the JotmFactoryBean implements both the UserTransaction and the TransactionManager interface --> <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="userTransaction" ref="jotm"/> <property name="allowCustomIsolationLevels" value="true"/> </bean> <!-- settings previously specified in the persistence.xml JPA configuration file are now defined with the LocalContainerEntityManagerFactoryBean configuration --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <!-- reference to the XA datasource --> <property name="dataSource" ref="bdbDataSource"/> <!-- specify Hibernate as the the JPA provider --> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true"/> <property name="generateDdl" value="false"/> <property name="database" value="ORACLE"/> <property name="databasePlatform" value="org.hibernate.dialect.OracleDialect"/> </bean> </property> <!-- configure Hibernate to participate in JTA transactions using the JOTM transaction manager and specify further Hibernate specific configuration properties --> <property name="jpaPropertyMap"> <map> <entry key="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JOTMTransactionManagerLookup"/> <entry key="hibernate.transaction.flush_before_completion" value="true"/> <entry key="hibernate.transaction.auto_close_session" value="true"/> <entry key="hibernate.current_session_context_class" value="jta"/> <entry key="hibernate.connection.release_mode" value="auto"/> </map> </property> <!-- specify that the Hibernate JPA dialect should be used, probably not necessary since HibernateJpaVendorAdapter will most likely set this property --> <property name="jpaDialect"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/> </property> <!-- custom implementation to enrich the PersistenceUnitInfo read from the persistence.xml JPA configuration file with the JTA datasource. specifying the JTA datasource directly in the Spring configuration file has the advantage that we can use a direct reference to the datasource instead of using a JNDI name as requied by the jta-data-source setting in the persistence.xml file --> <property name="persistenceUnitPostProcessors"> <bean class="sample.JtaPersistenceUnitPostProcessor"> <property name="jtaDataSource" ref="bdbDataSource"/> </bean> </property> </bean> <!-- enables interpretation of the @Transactional annotation for declerative transaction managment using the specified JtaTransactionManager --> <tx:annotation-driven transaction-manager="jtaTransactionManager" proxy-target-class="false"/> <!-- enables interpretation of the @Configurable annotation for domain object dependency injection --> <aop:spring-configured/> </beans>
sample.JtaPersistenceUnitPostProcessor.java
package sample;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
/**
* the {@link JtaPersistenceUnitPostProcessor} enables us to define the JTA capable datasource
* which should be used by the JPA provider directly as a reference within the Spring configuration
* file.
*/
public class JtaPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
/**
* a reference to the JTA capable datasource which is add to the PersistenceUnitInfo during post
* processing instead of beeing specified using the "jta-data-source" setting in the persistence.xml
* configuration file
*/
private DataSource jtaDataSource;
/**
* enrich the PersistenceUnitInfo read from the persistence.xml configuration file with a reference
* to the jtaDataSource injected via the Spring configuration. the JTA capable datasource is then
* used by the LocalContainerEntityManagerFactoryBean to create the EntityManagerFactory
*
* @see PersistenceUnitPostProcessor#postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo)
*/
public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo mutablePersistenceUnitInfo) {
mutablePersistenceUnitInfo.setJtaDataSource(getJtaDataSource());
}
/**
* getter for jtaDataSource
*
* @return the JTA capable datasource supplied via the setter
*/
public DataSource getJtaDataSource() {
return jtaDataSource;
}
/**
* setter for jtaDataSource
*
* @param jtaDataSource the JTA capable datasource added to the PersistenceUnitInfo during post processing
*/
@Required
public void setJtaDataSource(DataSource jtaDataSource) {
this.jtaDataSource = jtaDataSource;
}
}
if you don't want to implement and use a custom PersistenceUnitPostProcessor you can add the jta-data-source setting to the persistence-unit configuration in the persistence.xml file. add the following snippet and specify the JNDI name to the JTA capable datasource:
<jta-data-source>jdbc/bdbDataSource</jta-data-source>
you will then have to expose the JTA datasource via JNDI which can be achieved by using the XBean library and adding the following snippet to the spring-context.xml file:
<bean id="jndiContext" class="org.apache.xbean.spring.jndi.SpringInitialContextFactory" factory-method="makeInitialContext" scope="singleton" lazy-init="false"> <property name="entries"> <map> <entry key="jdbc/bdbDataSource" value-ref="bdbDataSource"/> </map> </property> </bean>
if you plan to use a JTA transaction manager other than the standalone JOTM transaction manager you will have to adapt the XA datasource configuration, most likely use the autodetect mode of the JtaTransactionManager by removing the JotmFactoryBean (or specifying another factory bean), and define a different hibernate transaction manager lookup class. for in container deployment you will most likely want to use the JndiObjectFactoryBean to integrate the datasources already configured within the application server into the Spring application context (by simply specifying the JNDI name).
本文介绍如何配置Spring框架以使用JPA和Hibernate进行数据访问操作,包括使用JpaTransactionManager和JtaTransactionManager进行事务管理的具体配置方法。
737

被折叠的 条评论
为什么被折叠?



