项目结构目录
在application.properties进行配置
#分布式事务进行管理
#MySql 1
mysql.datasource.test1.url = jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test1.username = root
mysql.datasource.test1.password = root
mysql.datasource.test1.minPoolSize = 3
mysql.datasource.test1.maxPoolSize = 25
mysql.datasource.test1.maxLifetime = 20000
mysql.datasource.test1.borrowConnectionTimeout = 30
mysql.datasource.test1.loginTimeout = 30
mysql.datasource.test1.maintenanceInterval = 60
mysql.datasource.test1.maxIdleTime = 60
mysql.datasource.test1.testQuery = select 1
# Mysql 2
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =root
mysql.datasource.test2.minPoolSize = 3
mysql.datasource.test2.maxPoolSize = 25
mysql.datasource.test2.maxLifetime = 20000
mysql.datasource.test2.borrowConnectionTimeout = 30
mysql.datasource.test2.loginTimeout = 30
mysql.datasource.test2.maintenanceInterval = 60
mysql.datasource.test2.maxIdleTime = 60
mysql.datasource.test2.testQuery = select 1
pom.xml配置
4.0.0
cn.mywork
springboot
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
1.5.8.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-tomcat
org.springframework.boot
spring-boot-starter-jdbc
mysql
mysql-connector-java
5.1.21
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.apache.tomcat.embed
tomcat-embed-jasper
org.springframework.boot
spring-boot-starter-data-jpa
org.mybatis
mybatis
3.4.5
org.springframework.boot
spring-boot-starter-jta-atomikos
org.springframework.boot
spring-boot-configuration-processor
true
分布式事务用分包管理的方式,即新建两个包user1和user2,包的路径下分别创建dao和service,用来存放数据库操作的类和事务管理的类,以对不同数据源进行区分操作
user1包下dao层
package cn.mywork.user1.dao;
import cn.mywork.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author Terry
* @version 1.0
* @date 2018-04-22 14:02
*/
public interface UserDAOTest1 extends JpaRepository {
}
service层
package cn.mywork.user1.service;
import cn.mywork.dao.UserDAO;
import cn.mywork.entity.User;
import cn.mywork.user1.dao.UserDAOTest1;
import cn.mywork.user2.dao.UserDAOTest2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Terry
* @version 1.0
* @date 2018-04-22 11:09
*/
@Service
public class UserService {
@Autowired
private UserDAOTest1 userDAOTest1;
@Autowired
private UserDAOTest2 userDAOTest2;
@Transactional
public String insert(User user){
userDAOTest1.save(user);
userDAOTest2.save(user);
int i=1/0;
return "success";
}
}
user2的dao层
package cn.mywork.user2.dao;
import cn.mywork.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author Terry
* @version 1.0
* @date 2018-04-22 14:04
*/
public interface UserDAOTest2 extends JpaRepository {
}
datasource包下,对两个数据源进行配置管理
package cn.mywork.datasource;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.sql.SQLException;
/**
* @author Terry
* @version 1.0
* @date 2018-04-22 15:15
*/
@ConfigurationProperties(prefix = "mysql.datasource.test1")
@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "cn.mywork.user1", sqlSessionTemplateRef = "user1SqlSessionTemplate")
public class DBConfig1 {
private String url;
private String username;
private String password;
private int minPoolSize;
private int maxPoolSize;
private int maxLifetime;
private int borrowConnectionTimeout;
private int loginTimeout;
private int maintenanceInterval;
private int maxIdleTime;
private String testQuery;
@Primary
@Bean(name ="user1DataSource")
public DataSource user1DataSource()throws SQLException {
MysqlXADataSource mysqlXADataSource=new MysqlXADataSource();
mysqlXADataSource.setUrl(getUrl());
mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXADataSource.setPassword(getPassword());
mysqlXADataSource.setUser(getUsername());
mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
AtomikosDataSourceBean xaDataSource=new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXADataSource);
xaDataSource.setUniqueResourceName("user1DataSource");
xaDataSource.setMinPoolSize(getMinPoolSize());
xaDataSource.setMaxPoolSize(getMaxPoolSize());
xaDataSource.setMaxLifetime(getMaxLifetime());
xaDataSource.setBorrowConnectionTimeout(getBorrowConnectionTimeout());
xaDataSource.setLoginTimeout(getLoginTimeout());
xaDataSource.setMaintenanceInterval(getMaintenanceInterval());
xaDataSource.setMaxIdleTime(getMaxIdleTime());
xaDataSource.setTestQuery(getTestQuery());
return xaDataSource;
}
@Primary
@Bean (name="user1SqlSessionFactory")
public SqlSessionFactory user1SqlSessionTemplate(@Qualifier("user1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Primary
@Bean(name = "user1SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("user1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Override
public String toString() {
return "DBConfig1{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", minPoolSize=" + minPoolSize +
", maxPoolSize=" + maxPoolSize +
", maxLifetime=" + maxLifetime +
", borrowConnectionTimeout=" + borrowConnectionTimeout +
", loginTimeout=" + loginTimeout +
", maintenanceInterval=" + maintenanceInterval +
", maxIdleTime=" + maxIdleTime +
", testQuery='" + testQuery + '\'' +
'}';
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMinPoolSize() {
return minPoolSize;
}
public void setMinPoolSize(int minPoolSize) {
this.minPoolSize = minPoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getMaxLifetime() {
return maxLifetime;
}
public void setMaxLifetime(int maxLifetime) {
this.maxLifetime = maxLifetime;
}
public int getBorrowConnectionTimeout() {
return borrowConnectionTimeout;
}
public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
this.borrowConnectionTimeout = borrowConnectionTimeout;
}
public int getLoginTimeout() {
return loginTimeout;
}
public void setLoginTimeout(int loginTimeout) {
this.loginTimeout = loginTimeout;
}
public int getMaintenanceInterval() {
return maintenanceInterval;
}
public void setMaintenanceInterval(int maintenanceInterval) {
this.maintenanceInterval = maintenanceInterval;
}
public int getMaxIdleTime() {
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
public String getTestQuery() {
return testQuery;
}
public void setTestQuery(String testQuery) {
this.testQuery = testQuery;
}
}
package cn.mywork.datasource;
/**
* @author Terry
* @version 1.0
* @date 2018-04-22 20:50
*/
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.sql.SQLException;
@ConfigurationProperties(prefix = "mysql.datasource.test2")
@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "cn.mywork.user2", sqlSessionTemplateRef = "user2SqlSessionTemplate")
public class DBConfig2 {
private String url;
private String username;
private String password;
private int minPoolSize;
private int maxPoolSize;
private int maxLifetime;
private int borrowConnectionTimeout;
private int loginTimeout;
private int maintenanceInterval;
private int maxIdleTime;
private String testQuery;
@Bean(name ="user2DataSource")
public DataSource user1DataSource()throws SQLException {
MysqlXADataSource mysqlXADataSource=new MysqlXADataSource();
mysqlXADataSource.setUrl(getUrl());
mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXADataSource.setPassword(getPassword());
mysqlXADataSource.setUser(getUsername());
mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
AtomikosDataSourceBean xaDataSource=new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXADataSource);
xaDataSource.setUniqueResourceName("user2DataSource");
xaDataSource.setMinPoolSize(getMinPoolSize());
xaDataSource.setMaxPoolSize(getMaxPoolSize());
xaDataSource.setMaxLifetime(getMaxLifetime());
xaDataSource.setBorrowConnectionTimeout(getBorrowConnectionTimeout());
xaDataSource.setLoginTimeout(getLoginTimeout());
xaDataSource.setMaintenanceInterval(getMaintenanceInterval());
xaDataSource.setMaxIdleTime(getMaxIdleTime());
xaDataSource.setTestQuery(getTestQuery());
return xaDataSource;
}
@Bean (name="user2SqlSessionFactory")
public SqlSessionFactory user1SqlSessionTemplate(@Qualifier("user2DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
@Bean(name = "user1Sq2SessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("user2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Override
public String toString() {
return "DBConfig1{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", minPoolSize=" + minPoolSize +
", maxPoolSize=" + maxPoolSize +
", maxLifetime=" + maxLifetime +
", borrowConnectionTimeout=" + borrowConnectionTimeout +
", loginTimeout=" + loginTimeout +
", maintenanceInterval=" + maintenanceInterval +
", maxIdleTime=" + maxIdleTime +
", testQuery='" + testQuery + '\'' +
'}';
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMinPoolSize() {
return minPoolSize;
}
public void setMinPoolSize(int minPoolSize) {
this.minPoolSize = minPoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getMaxLifetime() {
return maxLifetime;
}
public void setMaxLifetime(int maxLifetime) {
this.maxLifetime = maxLifetime;
}
public int getBorrowConnectionTimeout() {
return borrowConnectionTimeout;
}
public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
this.borrowConnectionTimeout = borrowConnectionTimeout;
}
public int getLoginTimeout() {
return loginTimeout;
}
public void setLoginTimeout(int loginTimeout) {
this.loginTimeout = loginTimeout;
}
public int getMaintenanceInterval() {
return maintenanceInterval;
}
public void setMaintenanceInterval(int maintenanceInterval) {
this.maintenanceInterval = maintenanceInterval;
}
public int getMaxIdleTime() {
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
public String getTestQuery() {
return testQuery;
}
public void setTestQuery(String testQuery) {
this.testQuery = testQuery;
}
}
根据实体类分别在数据库里面创建两个user表
package cn.mywork.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author Terry
* @version 1.0
* @date 2018-04-22 12:55
*/
@Entity(name="users")
public class User {
@Id
@GeneratedValue
private Integer id;
@Column
private String name;
@Column
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
控制层
package cn.mywork.controller;
import cn.mywork.entity.User;
import cn.mywork.user1.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Terry
* @version 1.0
* @date 2018-04-22 10:43
*/
@RestController
public class IndexController {
@Autowired
private UserService userService;
@RequestMapping("test")
@ResponseBody
public String insert (){
String message = userService.insert(new User("zhangsan", 15));
return message;
}
}
package cn.mywork.Application;
import cn.mywork.datasource.DBConfig1;
import cn.mywork.datasource.DBConfig2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
/**
* @author Terry
* @version 1.0
* @date 2018-04-22 10:46
*/
@ComponentScan(basePackages = {"cn.mywork.controller","cn.mywork.service","cn.mywork.user1.service"})
@EnableAutoConfiguration
@EnableJpaRepositories(basePackages ={"cn.mywork.dao","cn.mywork.user1.dao","cn.mywork.user2.dao"})
@EntityScan("cn.mywork.entity")
@EnableConfigurationProperties(value = {DBConfig1.class, DBConfig2.class })
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
运行程序,访问 localhost:8080/test
调用test接口,由于在service类中的insert方法,int i=1/0;程序肯定会报错的
然后就可以看到报错的页面,返回数据库里面看看,分别查看test1 和test2数据库里面的user表,
你会发现插入数据失败,可以肯定,由于调用了事务管理,两个数据源的事务回滚了,这就是想要的效果。
注意:进行事务操作要在datasource指定的包里面操作,否则达不到效果