最近刚搭建一个Atomikos实现的JTA,数据库持久层用的spring-data-jpa,底层实现是Hibernate。
一.环境
spring 3.2.6.RELEASE
hibernate 4.2.8.Final
spring-data-jpa 1.4.3.RELEASE
atomikos.version 3.9.2
永久链接: http://sgq0085.iteye.com/blog/2001918
atomikos在Maven环境中导入依赖如下
<!-- atomikos --> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>3.9.2</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-hibernate3</artifactId> <version>3.9.2</version> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency>
atomikos依赖结构如下
[INFO] +- com.atomikos:transactions-jdbc:jar:3.9.2:compile [INFO] | \- com.atomikos:transactions-jta:jar:3.9.2:compile [INFO] | \- com.atomikos:transactions:jar:3.9.2:compile [INFO] | \- com.atomikos:transactions-api:jar:3.9.2:compile [INFO] | \- com.atomikos:atomikos-util:jar:3.9.2:compile [INFO] +- com.atomikos:transactions-hibernate3:jar:3.9.2:compile [INFO] +- javax.transaction:jta:jar:1.1:compile
二.配置
1.web.xml
spring.profiles.default为了配合配置文件applicationContext.xml中<beans profile="development">标签,用于管理多套环境
<context-param> <param-name>spring.profiles.default</param-name> <param-value>development</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:/applicationContext.xml </param-value> </context-param> <!-- 不一定都用得到 --> <filter> <filter-name>openEntityManagerInViewFilter</filter-name> <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class> <init-param> <param-name>entityManagerFactoryBeanName</param-name> <param-value>entityManagerFactory1</param-value> </init-param> </filter> <filter-mapping> <filter-name>openEntityManagerInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
2.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_2_0.xsd" version="2.0"> <persistence-unit name="PU1" transaction-type="JTA"> </persistence-unit> <persistence-unit name="PU2" transaction-type="JTA"> </persistence-unit> </persistence>
这里JPA 不在 persistence.xml 文件中配置每个Entity实体类,在后面的applicationContext.xml中通过<property name="packagesToScan" value="com.myapp" />去扫描所有标记@Entity注解的PO。
3.application.properties
数据库一个Oracle,一个H2
#development dev.jdbc.driver=oracle.jdbc.driver.OracleDriver dev.jdbc.url=jdbc:oracle:thin:@192.168.3.129:1521:gtf dev.jdbc.username=my_app dev.jdbc.password=my_app dev.jdbc.driver2=org.h2.jdbcx.JdbcDataSource dev.jdbc.url2=jdbc:h2:file:~/.h2/myapp;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE dev.jdbc.username2=sa dev.jdbc.password2= databasePlatform.oracle=org.hibernate.dialect.Oracle10gDialect databasePlatform.h2=org.hibernate.dialect.H2Dialect
4.applicationContext.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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd"> <description>Spring公共配置</description> <!-- 使用annotation 自动注册bean,并检查@Required,@Autowired的属性已被注入 --> <context:component-scan base-package="com.gqshao.myapp"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" /> </context:component-scan> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout" value="300" /> </bean> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <property name="forceShutdown" value="true" /> <property name="transactionTimeout" value="300" /> </bean> <!-- 事务管理器配置, JTA数据源事务 --> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="atomikosTransactionManager" /> <property name="userTransaction" ref="atomikosUserTransaction" /> <property name="allowCustomIsolationLevels" value="true" /> </bean> <!-- 使用annotation定义事务 --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> <!-- 定义aspectj --> <aop:aspectj-autoproxy proxy-target-class="true" /> <bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <!-- <property name="databasePlatform" value="${oracle.databasePlatform}" /> --> <property name="databasePlatform"> <bean factory-method="getDialect" class="com.gqshao.myapp.common.persistence.Hibernates"> <constructor-arg ref="dataSource" /> </bean> </property> <property name="showSql" value="false" /> <property name="generateDdl" value="true" /> <property name="database" value="ORACLE" /> </bean> <!-- Jpa Entity Manager1 配置 --> <bean id="entityManagerFactory1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceXmlLocation" value="classpath*:/persistence.xml" /> <property name="persistenceUnitName" value="PU1" /> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" /> <property name="packagesToScan" value="com.gqshao.myapp" /> <property name="jpaProperties"> <props> <!-- 不开启二级缓存 --> <prop key="hibernate.cache.use_second_level_cache">false</prop> <prop key="hibernate.generate_statistics">false</prop> <prop key="hibernate.connection.release_mode">after_statement</prop> <!-- 命名规则 My_NAME->MyName --> <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">none</prop> <prop key="hibernate.connection.driver_class">oracle.jdbc.xa.client.OracleXADataSource</prop> <prop key="hibernate.current_session_context_class">jta</prop> <prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop> </props> </property> </bean> <!-- Spring Data Jpa配置, 扫描base-package下所有继承于Repository<T,ID>的接口 --> <jpa:repositories base-package="com.gqshao.myapp.**.dao" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory1" /> <!-- Jpa Entity Manager2 配置 --> <bean id="entityManagerFactory2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceXmlLocation" value="classpath*:/persistence.xml" /> <property name="persistenceUnitName" value="PU2" /> <property name="dataSource" ref="dataSource2" /> <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" /> <property name="packagesToScan" value="com.gqshao.myapp" /> <property name="jpaProperties"> <props> <!-- 不开启二级缓存 --> <prop key="hibernate.cache.use_second_level_cache">false</prop> <prop key="hibernate.generate_statistics">false</prop> <prop key="hibernate.connection.release_mode">after_statement</prop> <!-- 命名规则 My_NAME->MyName --> <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">none</prop> <prop key="hibernate.connection.driver_class">oracle.jdbc.xa.client.OracleXADataSource</prop> <prop key="hibernate.current_session_context_class">jta</prop> <prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop> </props> </property> </bean> <!-- Spring Data Jpa配置, 扫描base-package下所有继承于Repository<T,ID>的接口 --> <jpa:repositories base-package="com.gqshao.myapp.**.repository" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory2" /> <!-- local development环境 --> <beans profile="development"> <context:property-placeholder location="classpath*:/application.properties" ignore-resource-not-found="true" /> <!-- 数据源1 --> <bean id="dataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="XA1DBMS" /> <property name="xaDataSourceClassName" value="${dev.jdbc.driver}" /> <property name="xaProperties"> <props> <prop key="URL">${dev.jdbc.url}</prop> <prop key="user">${dev.jdbc.username}</prop> <prop key="password">${dev.jdbc.password}</prop> </props> </property> <property name="poolSize" value="10" /> <property name="minPoolSize" value="10" /> <property name="maxPoolSize" value="30" /> </bean> <!-- 数据源2 --> <bean id="dataSource2" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName" value="XA2DBMS" /> <property name="xaDataSourceClassName" value="${dev.jdbc.driver2}" /> <property name="xaProperties"> <props> <prop key="URL">${dev.jdbc.url2}</prop> <prop key="user">${dev.jdbc.username2}</prop> <prop key="password">${dev.jdbc.password2}</prop> </props> </property> <property name="poolSize" value="10" /> <property name="minPoolSize" value="10" /> <property name="maxPoolSize" value="30" /> </bean> </beans> </beans>
注意:
1.Entity可以被两个EntityManagerFactory同时扫描到
2.通过jpa:repositories的base-package来区分两个EntityManagerFactory维护的持久层在这里这个用dao包,另一个用repository
三.测试
-------------- 表1 ------------ create table jta_pu1 ( id char(32), description varchar2(255), primary key (id) ); -------------- 表2 ------------ create table jta_pu2 ( id char(32), description varchar2(255), primary key (id) );
在Oracle中建表1
在H2中表1和表2都建
三个Entity类
package com.myapp.jta.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import org.hibernate.annotations.GenericGenerator; @MappedSuperclass public abstract class BaseEntity implements Serializable { private static final long serialVersionUID = 1L; protected String id; @Id @Column(name = "id", nullable = false) @GeneratedValue(generator = "system-uuid") @GenericGenerator(name = "system-uuid", strategy = "uuid") public String getId() { return id; } public void setId(String id) { this.id = id; } }
package com.myapp.jta.entity; import javax.persistence.Entity; import javax.persistence.Table; @Entity @Table(name = "JTA_PU1") public class PU1 extends BaseEntity { private String description; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
package com.myapp.jta.entity; import javax.persistence.Entity; import javax.persistence.Table; @Entity @Table(name = "JTA_PU2") public class PU2 extends BaseEntity { private String description; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
三个数据库持久层类
对应数据源一
package com.myapp.jta.dao; import org.springframework.data.jpa.repository.JpaRepository; import com.myapp.jta.entity.PU1; public interface PU1Dao extends JpaRepository<PU1, Integer> { }
对应数据源二
package com.myapp.jta.repository; import org.springframework.data.jpa.repository.JpaRepository; import com.myapp.jta.entity.PU1; public interface PU1Repository extends JpaRepository<PU1, Integer> { }
package com.myapp.jta.repository; import org.springframework.data.jpa.repository.JpaRepository; import com.myapp.jta.entity.PU2; public interface PU2Repository extends JpaRepository<PU2, Integer> { }
注意源一对应的包名是dao,源二对应的包名是repository
Service比较简单只有一个save,第一次不抛出异常,第二次抛出异常来测试事务回滚。
package com.myapp.jta.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.myapp.jta.dao.PU1Dao; import com.myapp.jta.entity.PU1; import com.myapp.jta.entity.PU2; import com.myapp.jta.repository.PU1Repository; import com.myapp.jta.repository.PU2Repository; @Service @Transactional public class JTAService { @Value("${property.value}") private String propertyValue; @Autowired private PU1Dao pu1Dao; @Autowired private PU1Repository pu1Repository; @Autowired private PU2Repository pu2Repository; public void save() { PU1 pu = new PU1(); pu.setDescription(propertyValue); pu1Dao.save(pu); pu1Repository.save(pu); PU2 pu2 = new PU2(); pu2.setDescription("pu2"); pu2Repository.save(pu2); // throw new RuntimeException(); } }
JUnit4的测试用例
package com.myapp.jta.service; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "/applicationContext.xml"}) @ActiveProfiles("development") @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false) public class JTAServiceTest { @Autowired private JTAService jtaService; @Test public void savePU1() { jtaService.save(); } }
说明,同一个EntityPU1分别被保存到数据源一和数据源二中,但通过查询数据库可以看到第一次保存的时候id被赋值,第二次保存的时候id没有再做修改。但查询数据库,发现第二次的id与第一次的不同。所以真有同一个Entity保存到两个数据库中的需求时,推荐在保存之前深拷贝一份出来,分别保存。
永久链接: http://sgq0085.iteye.com/blog/2001918
注意两个离奇错误
错误1: 数据库必须是DBA,否则出错 (具体哪个权限没有去排查,感兴趣找到了告诉我)
错误2:用maven tomcat7-maven-plugin插件时小心useTestClasspath需要是false
DEMO中在使用jdbcdslog的基础上,修改Atomikos核心类,实现JTA管理数据源下SQL的输出。
注意:该方法未经过大量测试,只适用于开发环境,生产环境不可以使用。
相关推荐
* spring-boot-starter-jta-atomikos:提供了对 Atomikos 事务管理器的支持。 * spring-boot-starter-jta-bitronix:提供了对 Bitronix 事务管理器的支持。 这些启动器之间的交互和协作是非常复杂的,每个启动器都...
spring-boot-starter-jta-atomikos spring-boot-starter-jta-bitronix spring-boot-starter-mail spring-boot-starter-mobile spring-boot-starter-mustache spring-boot-starter-...
1. **JTA事务管理**: 配置Spring Boot以使用JTA事务管理,需要引入`spring-boot-starter-jta-atomikos`或`spring-boot-starter-jta-bitronix`依赖,并配置相应的事务管理器。 2. **Atomikos和Bitronix**: Atomikos...
29.3. JPA and “Spring Data” 29.3.1. Entity Classes 29.3.2. Spring Data JPA Repositories 29.3.3. Creating and Dropping JPA Databases 29.3.4. Open EntityManager in View 29.4. Using H2’s Web Console ...
* spring-boot-starter-jta-atomikos:通过Atomikos支持JTA分布式事务处理。 * spring-boot-starter-jta-bitronix:通过Bitronix支持JTA分布式事务处理。 * spring-boot-starter-mail:支持javax.mail模块。 * ...
在现代项目中,我们通常会使用更兼容的版本或Spring Data JPA来配合JTA事务。 3. **JTA事务管理**: JTA定义了用户事务API(`UserTransaction`接口)和事务管理器API(`TransactionManager`接口)。`...
在压缩包文件 "springjta" 中,可能包含了一些关于 Spring JTA(Java Transaction API)的资料,JTA 是 Java 平台提供的分布式事务处理标准,Spring JPA 在需要时可以与之配合,实现跨多个数据源的事务一致性。...
通过这个项目,开发者可以深入理解Springboot的事务管理、JPA的使用,以及Atomikos如何实现JTA分布式事务。这不仅有助于提升技术水平,也为实际项目中的分布式事务处理提供了参考和实践案例。无论是对新手还是经验...
10. **JTA(Java Transaction API)事务管理**:对于分布式事务,Spring支持JTA事务管理,如Atomikos、Bitronix等事务管理器,适用于跨越多个数据源的复杂场景。 11. **事务同步**:Spring的Transaction...
2. 使用`@Primary`注解标记主数据源,用于Spring Data JPA或其他自动配置。 3. 使用`@Qualifier`注解来指定在Service层注入哪个数据源。 接下来,我们讨论分布式事务。在分布式系统中,事务需要跨越多个数据库或者...
使用Spring Data JPA和Mongo仓库 x. 67.10. 将Spring Data仓库暴露为REST端点 vii. 68. 数据库初始化 i. 68.1. 使用JPA初始化数据库 ii. 68.2. 使用Hibernate初始化数据库 iii. 68.3. 使用Spring JDBC初始化数据库 ...
对于分布式事务,Spring支持JTA和XAP(如Atomikos、Bitronix)。 7. **安全集成**:Spring Security提供了全面的身份验证和授权机制,可以保护Web应用免受各种攻击。它支持HTTP基本认证、表单登录、OAuth2和其他...
- **第二种方式**:利用`jta-atomikos`等分布式事务管理库实现。这种方式适用于需要跨数据源的事务管理场景,如微服务架构下的分布式事务。通过Atomikos等工具可以有效地协调不同数据源之间的事务提交与回滚。 ####...
特别是在处理数据库操作时,SpringBoot结合Spring Data JPA或MyBatis等持久层框架,提供了高效且灵活的数据管理方式。本教程将深入探讨如何在SpringBoot项目中实现数据同步,连接并管理多个MySQL数据库。 首先,...