您当前的位置: 首页 >  微服务

微服务技术系列教程(04) - SpringBoot - 事务管理

杨林伟 发布时间:2019-11-06 14:05:06 ,浏览量:2

代码已上传到Github,有兴趣的同学可以下载来看看:https://github.com/ylw-github/SpringBoot-Transaction-Demo

1. SpringBoot事务管理

SpringBoot默认集成事务,只主要在方法上加上@Transactional即可

操作比较简单,此处不再详述,可以参考:https://blog.csdn.net/justry_deng/article/details/80828180

2. SpringBoot分布式事务管理

可以使用springboot+jta+atomikos 进行分布式事务管理,下面来详细介绍集成的步骤:

2.1 添加mave依赖

	org.springframework.boot
	spring-boot-starter-parent
	2.0.0.RELEASE




	
		org.springframework.boot
		spring-boot-starter-jta-atomikos
	
	
	
		org.springframework.boot
		spring-boot-starter
	
	
	
	
		org.springframework.boot
		spring-boot-starter-test
		test
	
	
	
		org.mybatis.spring.boot
		mybatis-spring-boot-starter
		1.1.1
	
	
	
	
		mysql
		mysql-connector-java
	
	
	
	
		org.springframework.boot
		spring-boot-starter-web
	

2.2 配置application.properties
# Mysql 1
mysql.datasource.test1.url = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test1.username = root
mysql.datasource.test1.password = 123456
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 2
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =123456
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
2.3 ConfigurationProperties

总共要写两个配置类DBConfig1和DBConfig2:

@ConfigurationProperties(prefix = "mysql.datasource.test1")
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;
 	//getter setter...

//DBConfig2///

@ConfigurationProperties(prefix = "mysql.datasource.test2")
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;
	//getter setter...
2.4 配置数据源

配置的时候注意,每个扫描mapper的包位置不一致。

数据源1:

package com.ylw.datasource;

@Configuration
// basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "com.ylw.mapper.test01", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class MyBatisConfig1 {

	// 配置数据源

	@Bean(name = "testDataSource")
	public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
		MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
		mysqlXaDataSource.setUrl(testConfig.getUrl());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
		mysqlXaDataSource.setPassword(testConfig.getPassword());
		mysqlXaDataSource.setUser(testConfig.getUsername());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

		// 将本地事务注册到创 Atomikos全局事务
		AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
		xaDataSource.setXaDataSource(mysqlXaDataSource);
		xaDataSource.setUniqueResourceName("testDataSource");

		xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
		xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
		xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
		xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
		xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
		xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
		xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
		xaDataSource.setTestQuery(testConfig.getTestQuery());
		return xaDataSource;
	}

	@Bean(name = "testSqlSessionFactory")
	public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource") DataSource dataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}

	@Bean(name = "testSqlSessionTemplate")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("testSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

数据源2:

@Configuration
@MapperScan(basePackages = "com.ylw.mapper.test02", sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class MyBatisConfig2 {

	// 配置数据源
	@Bean(name = "test2DataSource")
	public DataSource testDataSource(DBConfig2 testConfig) throws SQLException {
		MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
		mysqlXaDataSource.setUrl(testConfig.getUrl());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
		mysqlXaDataSource.setPassword(testConfig.getPassword());
		mysqlXaDataSource.setUser(testConfig.getUsername());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

		AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
		xaDataSource.setXaDataSource(mysqlXaDataSource);
		xaDataSource.setUniqueResourceName("test2DataSource");

		xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
		xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
		xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
		xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
		xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
		xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
		xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
		xaDataSource.setTestQuery(testConfig.getTestQuery());
		return xaDataSource;
	}

	@Bean(name = "test2SqlSessionFactory")
	public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}

	@Bean(name = "test2SqlSessionTemplate")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}
2.5 配置Mapper

注意包结构不一样。

UserMapperTest01:

package com.ylw.mapper.test01;

public interface UserMapperTest01 {

	// 查询语句
	@Select("SELECT * FROM t_user WHERE name = #{name}")
	User findByName(@Param("name") String name);

	// 添加
	@Insert("INSERT INTO t_user(uuid,name, age) VALUES(#{uuid},#{name}, #{age})")
	int insert(@Param("uuid") String uuid,@Param("name") String name, @Param("age") Integer age);
}

UserMapperTest02:

package com.ylw.mapper.test02;

public interface UserMapperTest02 {
	// 查询语句
	@Select("SELECT * FROM t_user WHERE name = #{name}")
	User findByName(@Param("name") String name);

	// 添加
	@Insert("INSERT INTO t_user(uuid,name, age) VALUES(#{uuid},#{name}, #{age})")
	int insert(@Param("uuid") String uuid,@Param("name") String name, @Param("age") Integer age);
}

2.6 业务类

UserServiceTest01:

package com.ylw.service.test01;

@Service
public class UserServiceTest01 {
    @Autowired
    private UserMapperTest01 userMapperTest01;

    @Transactional
    public int insertUser(String name, Integer age) {
        int insertUserResult = userMapperTest01.insert(UUID.randomUUID().toString(), name, age);
        System.out.println("######insertUserResult:{}##########-> " + insertUserResult);
        //int i = 1 / age;
        // 验证事务开启
        return insertUserResult;
    }

}

UserServiceTest02:

package com.ylw.service.test02;

@Service
public class UserServiceTest02 {
    @Autowired
    private UserMapperTest02 userMapperTest02;
    @Autowired
    private UserMapperTest01 userMapperTest01;

    @Transactional
    public int insertUser(String name, Integer age) {
        int insertUserResult = userMapperTest02.insert(UUID.randomUUID().toString(), name, age);
        System.out.println("######insertUserResult:{}########## -> " + insertUserResult);
        // 怎么样验证事务开启成功!~
        //int i = 1 / age;
        return insertUserResult;
    }

    @Transactional()
    public int insertUserTest01AndTest02(String name, Integer age) {
        // 传统分布式事务解决方案 jta+atomikos 注册同一个全局事务中
        // 第一个数据源
        int insertUserResult01 = userMapperTest01.insert(UUID.randomUUID().toString(), name, age);
        // 第二个数据源
        int insertUserResult02 = userMapperTest02.insert(UUID.randomUUID().toString(), name, age);
        //int i = 1 / 0;
        int result = insertUserResult01 + insertUserResult02;
        // test01入库 test02回滚
        return result;
    }

}

2.7 启动类与Controller

启动类,要配置EnableConfigurationProperties:

// 开启读取配置文件
@EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class })
@SpringBootApplication
public class MybatisApp03 {

	public static void main(String[] args) {
		SpringApplication.run(MybatisApp03.class, args);
	}
}

controller:


@RestController
public class MybatisMultilDataSourceController {

	@Autowired
	private UserServiceTest01 userServiceTest01;
	@Autowired
	private UserServiceTest02 userServiceTest02;

	@RequestMapping(value = "/insertUserTest1" ,method = RequestMethod.GET)
	public Integer insertUserTest1(String name, Integer age) {
		return userServiceTest01.insertUser(name, age);
	}

	@RequestMapping(value = "/insertUserTest2",method = RequestMethod.GET)
	public Integer insertUserTest2(String name, Integer age) {
		return userServiceTest02.insertUser(name, age);
	}

	@RequestMapping(value ="/insertUserTest01AndTest02",method = RequestMethod.GET)
	public int insertUserTest01AndTest02(String name, Integer age) {
		return userServiceTest02.insertUserTest01AndTest02(name, age);
	}
}

2.8 测试

在浏览器打开三个页面,地址分别是:

  • http://localhost:8080/insertUserTest1?name=name1&age=10
  • http://localhost:8080/insertUserTest2?name=name2&age=18
  • http://localhost:8080/insertUserTest01AndTest02?name=name1AndName2&age=19

运行后,会发现两个数据库均添加了数据: 在这里插入图片描述 在这里插入图片描述

总结

在这里插入图片描述

关注
打赏
1688896170
查看更多评论

杨林伟

暂无认证

  • 2浏览

    0关注

    3183博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文
立即登录/注册

微信扫码登录

0.2012s