jta: Java Transactio API,即是java中对事务处理的api,api即是接口的意思.
atomikos:Atomikos TransactionsEssentials 是一个为Java平台提供增值服务的并且开源类事务管理器,基于2PC协议.不明白XA 2PC 3PC TCC的同学可以花几分钟的时间去看一下.
[这里由于是demo,我在其父工程中指明了版本号.各位同学在学习的时候,可以自己选择版本号]
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jta-atomikosartifactId>
dependency>
dependencies>
server:
port: 8081 #应用端口号
spring:
application:
name: seata-storage #应用名称
jpa: #jpa相关配置
hibernate:
ddl-auto: none
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
#format_sql: true
transaction:
jta:
platform: com.ajc.seata.config.AtomikosJtaPlatform #处理事务的类 hibernate不提供 需要自己写 需要继承 AbstractJtaPlatform[这里要注意 这里写的是这个类的全路径]
javax:
persistence:
transactionType: JTA #指明事务处理类型
jta: #jta相关配置
enabled: true #允许使用jta
atomikos: #atomikos相关配置 主要是配置数据源
datasource: #模拟多数据源
jta-bank1: #数据源1 名字自定义 唯一就好
xa-properties.url: jdbc:mysql://xx.xx.xx.xx:3306/bank1
xa-properties.user: root
xa-properties.password: 123456
xa-data-source-class-name: com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
unique-resource-name: jta-bank1
uniqueResourceName: jta-bank1
max-pool-size: 25
min-pool-size: 3
max-lifetime: 20000
borrow-connection-timeout: 10000
jta-bank2: #数据源2 名字自定义 唯一就好
xa-properties.url: jdbc:mysql://xx.xx.xx.xx:3306/bank2
xa-properties.user: root
xa-properties.password: 123456
xa-data-source-class-name: com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
unique-resource-name: jta-bank2
uniqueResourceName: jta-bank2
max-pool-size: 25
min-pool-size: 3
max-lifetime: 20000
borrow-connection-timeout: 10000
import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
/**
* 处理事务的类 hibernate不提供 需要自己写
*/
public class AtomikosJtaPlatform extends AbstractJtaPlatform {
private static final long serialVersionUID = 1L;
//事务管理器 atomikos的userTransactionManager
public static TransactionManager transactionManager;
//atomikos的事务管理的相关配置 比如超时时间 等
public static UserTransaction transaction;
@Override
protected TransactionManager locateTransactionManager() {
return transactionManager;
}
@Override
protected UserTransaction locateUserTransaction() {
return transaction;
}
}
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
/**
* @Author: AnJunchao
* @Description:
* @Date: Create in 2019-10-11 15:01:34
* @Modified By:
*/
@Configuration
@EnableConfigurationProperties
@EnableAutoConfiguration
@ComponentScan
@EnableTransactionManagement
public class DataSourceConfig {
/**
* transaction manager
*
* @return
*/
@Bean(name="userTransaction")
public UserTransaction userTransaction() {
UserTransactionImp userTransactionImp = new UserTransactionImp();
//配置事务超时时间
try {
userTransactionImp.setTransactionTimeout(500);
} catch (SystemException e) {
e.printStackTrace();
}
return userTransactionImp;
}
/**
* jta transactionManager spring对jta的支持
* @return AtomikosJtaPlatform.transactionManager = userTransactionManager;
*/
@Bean(destroyMethod = "close", initMethod = "init",name = "transactionManager")
public TransactionManager userTransactionManager() {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);//设置为true 网络出现问题时 强制断掉
AtomikosJtaPlatform.transactionManager = userTransactionManager;
return userTransactionManager;
}
/**
* 事务管理器 注意这个bean的名字 一会儿还要用到
*/
@Bean(name = "transactionManager")
@DependsOn({"userTransaction", "transactionManager"})
public PlatformTransactionManager transactionManager() throws Throwable {
UserTransaction userTransaction = userTransaction();
AtomikosJtaPlatform.transaction = userTransaction;
TransactionManager atomikosTransactionManager = userTransactionManager();
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}
}
该demo中我在spring的配置文件中定义了两个数据库,模拟一下多数据源jta-bank1和jta-bank2
每个数据源有一个相关的配置类,看代码:
import com.atomikos.jdbc.AtomikosDataSourceBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
/**
* @Author: AJC
* @Description:
* @Date: Create in 2019-10-12 09:33:59
* @Modified By:
*/
@Configuration
@EnableTransactionManagement
@DependsOn("transactionManager")
@EnableJpaRepositories(
entityManagerFactoryRef="bank1ManagerFactory",//指明实体的管理Factory 名字在本类配置的LocalContainerEntityManagerFactoryBean
transactionManagerRef="transactionManager",//这里注意了 这里的name就是上面DataSourceConfig这个类中事务管理器@Bean的name
basePackages= "com.ajc.seata.repository.bank1") //设置jta-bank1对应的Repository所在位置
public class Bank1Config {
/**
* 配置数据源 可以配置多个但必须用@Qualifier("xxx")指明
* @return
*/
@Primary//由多个数据源的时候 用这个指定默
@Bean
@ConfigurationProperties(prefix = "spring.jta.atomikos.datasource.jta-bank1") //以什么开头的spring文件中的配置路径
@Qualifier("dataSourceJTABank1")
public DataSource dataSourceJTABank1() {
return new AtomikosDataSourceBean();
}
@Autowired
JpaProperties jpaProperties;
//*--------- atomikos -----------*//*
@Primary //自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@Bean(name = "bank1ManagerFactory")
@DependsOn("transactionManager")
public LocalContainerEntityManagerFactoryBean bannk1ManagerFactoryDemo(EntityManagerFactoryBuilder builder) {
//如果在spring的配置文件中不指定这里可以用这种方式指明
// jpaProperties.getProperties().put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
return builder
.dataSource(dataSourceJTABank1())
.properties(jpaProperties.getProperties())
.packages("com.ajc.seata.entity.bank1") //设置实体类所在位置:类或包
.persistenceUnit("jta-bank1") //持久化单元名称
.build();
}
}
import com.atomikos.jdbc.AtomikosDataSourceBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
/**
* @Author: AJC
* @Description:
* @Date: Create in 2019-10-12 09:33:59
* @Modified By:
*/
@Configuration
@EnableTransactionManagement
@DependsOn("transactionManager")
@EnableJpaRepositories(
entityManagerFactoryRef="bank2ManagerFactory",
transactionManagerRef="transactionManager",
basePackages= "com.ajc.seata.repository.bank2")
public class Bank2Config {
@Autowired
JpaProperties jpaProperties;
@Bean
@ConfigurationProperties(prefix = "spring.jta.atomikos.datasource.jta-bank2")
@Qualifier("dataSourceJTABank2")
public DataSource dataSourceJTABank2() {
return new AtomikosDataSourceBean();
}
/*--------- atomikos -----------*/
@Bean(name = "bank2ManagerFactory")
@DependsOn("transactionManager")
public LocalContainerEntityManagerFactoryBean bannk2ManagerFactoryDemo(EntityManagerFactoryBuilder builder) {
//jpaProperties.getProperties().put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
return builder
.dataSource(dataSourceJTABank2())
.properties(jpaProperties.getProperties())
.packages("com.ajc.seata.entity.bank2") //设置实体类所在位置:类或包
.persistenceUnit("jta-bank2") //持久化单元名称
.build();
}
}
到此配置结束;
由于是多个数据源,所以不同的数据源的对应的entity和repository不能放在一起
注意 这里如果用本demo测试的话,看着实体的字段,自行创建数据表
这里bank1与bank2我用的表识一样的 这里为了分别区分,在实体名字后加了1和2;
AccountInfoBank1:
@Table(name = "account_info", schema = "bank1")
@Entity
@Data
public class AccountInfoBank1 implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "account_name")
private String accountName;
@Column(name = "account_no")
private String accountNo;
@Column(name = "account_password")
private String accountPassword;
@Column(name = "account_balance")
private Double accountBalance;
}
AccountInfoBank1:
@Table(name = "account_info", schema = "bank2")
@Entity
@Data
public class AccountInfoBank2 implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "account_name")
private String accountName;
@Column(name = "account_no")
private String accountNo;
@Column(name = "account_password")
private String accountPassword;
@Column(name = "account_balance")
private Double accountBalance;
}
AccountRepositoryBank1:
import com.ajc.seata.entity.bank1.AccountInfoBank1;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AccountRepositoryBank1 extends JpaRepository<AccountInfoBank1,Long> {
}
AccountRepositoryBank2:
import com.ajc.seata.entity.bank2.AccountInfoBank2;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AccountRepositoryBank2 extends JpaRepository<AccountInfoBank2,Long> {
}
service:
@Service
public class BankService {
@Autowired
private AccountRepositoryBank1 accountRepositoryBank1;
@Autowired
private AccountRepositoryBank2 accountRepositoryBank2;
@Transactional(rollbackFor = Exception.class)//事务注解不可少
public String updateBankofAccount(Double amount) {
//张三账户减少余额
AccountInfoBank1 accountInfoBank1 = accountRepositoryBank1.findById(2L).get();
accountInfoBank1.setAccountBalance(accountInfoBank1.getAccountBalance()-amount);
AccountInfoBank1 save = accountRepositoryBank1.saveAndFlush(accountInfoBank1);
if(amount==2){ //人为模拟异常
throw new RuntimeException("张三账户无法减少余额,访问失败");
}
//李四账户增加余额
AccountInfoBank2 accountInfoBank2 = accountRepositoryBank2.findById(3L).get();
accountInfoBank2.setAccountBalance(accountInfoBank2.getAccountBalance()+amount);
AccountInfoBank2 accountInfoBank21 = accountRepositoryBank2.saveAndFlush(accountInfoBank2);
if(amount==3){//人为模拟异常
throw new RuntimeException("李四账户无法减少余额,访问失败");
}
return "success";
}
}
controlelr:
@RestController
@RequestMapping("/bank")
public class BankController {
@Autowired
private BankService bankService;
@Autowired
JpaProperties jpaProperties;
@GetMapping("/updateBank")
public String updateBankofAccount(@RequestParam("amount") Double amount) throws HeuristicRollbackException, HeuristicMixedException, NotSupportedException, RollbackException, SystemException {
return bankService.updateBankofAccount(amount);
}
}
application:
@SpringBootApplication
@EnableDiscoveryClient
public class StorageApplication {
public static void main(String[] args) {
SpringApplication.run(StorageApplication.class,args);
}
}
到此启动测试就可以了.
demo代码地址:https://gitee.com/anjunchao/jta-atomikos