# 多数据源和事务使用文档
# 多数据源配置说明
在springboot的配置文件中添加数据源配置,或者启用mybatis配置文件
ts:
database:
mybatis:
datasourceList: tsds,ds2
tsds:
datasourceType: druid
page-dialect: mysql
url: jdbc:mysql://aliyuncs.com:3306/txs?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
driverClassName: com.mysql.jdbc.Driver
username:
password:
minimumIdle: 5
mappers:
- com.jinke.ts.module.mybatis.mapper
- com.jinke.ts.component.**.mapper.read
- com.jinke.ts.component.**.mapper.write
- com.jinke.ts.module.**.mapper.read
- com.jinke.ts.module.**.mapper.write
- com.jinke.ts.tsds.**.mapper.read
- com.jinke.ts.tsds.**.mapper.write
type-aliases-package:
- com.jinke.ts.tsds.**.entity
- com.jinke.ts.**.entity
mapper-locations:
- classpath*:mapper/read/*.xml
- classpath*:mapper/write/*.xml
transaction: #用于扩展事务管理器属性
enforceReadOnly: false #表示DataSourceTrasactionManager支持只读事务,某些数据库不支持此属性,需要关闭
ds2:
datasourceType: druid
page-dialect: mysql
url: jdbc:mysql://aliyuncs.com:3306/city?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
driverClassName: com.mysql.jdbc.Driver
username:
password:
minimumIdle: 5
mappers:
- com.jinke.ts.ds2.**.mapper.read
- com.jinke.ts.ds2.**.mapper.write
type-aliases-package:
- com.jinke.ts.ds2.**.entity
mapper-locations:
- classpath*:mapper/ds2/read/*.xml
- classpath*:mapper/ds2/write/*.xml
transaction: #用于扩展事务管理器属性
enforceReadOnly: false #表示DataSourceTrasactionManager支持只读事务,某些数据库不支持此属性,需要关闭
# 数据源事务说明
- 数据源和事务管理器是一一对应的,所有service都应该进行分类,一个service里面只能注入同一数据源的dao或者mapper,如果需要用到其他数据源,则应该以service的形式注入进去
- 所有事务均在service上去配置,不要在dao层去配置!
- @TsTransactional注解表示需要事务,使用的是mybatis,由框架自动分配事务管理器,@TsTransactional(value="mydsTransactionManager")手动指定事务管理器,此时框架将不会自动分配
- service上添加@NoTransactional注解明确表示不需要事务,框架将不对此service做任何处理
- 所有service均应该配置@TsTransactional或者@NoTransactional注解,否则项目启动将报错
- @TsTransactional(ormType = OrmTypeEnum.IBATIS)表示使用ibatis(不写默认表示使用mybatis)
# 框架默认包路径规则(针对于多数据源的情况)
- service上配置了@TsTransactional注解后,框架会自动解析,并自动分配事务管理器,规则为包路径上要包括数据源的beanName,例如:com.jinke.ts.module.ds1.service.impl,包路径中包括了“ds1”这个数据源,则自动分配“ds1TransactionManager”,相当于@TsTransactional("ds1TransactionManager")
- 如果包路径上没有对应数据源,则分配默认事务管理器“tsdsTransactionManager”
- 分配规则可自定义实现,需要实现DataSourceRegisterRule,添加注解@Component(value="projectDataSourceRegisterRule"),但原则上必须满足“事务管理器使用说明”的第一条。
# 事务检测
- 项目启动时,会自动检测事务,对于配置了@Service注解的,必须明确指定@TsTransactional,@NoTransactional两注解中的一个,否则报错
- 项目运行过程中,会对每次数据库操作做事务检测,当发现readOnly=false时,时select操作,则提醒这个地方需要优化
- 事务配置已经开启“强制读”操作,即当readOnly=true时,有增删改操作,直接报错。
# 事务管理器使用说明
- 数据源和事务管理器是一一对应的,所有service都应该进行分类,一个service里面只能注入同一数据源的dao或者mapper,如果需要用到其他数据源,则应该以service的形式注入进去
- 所有事务均在service上去配置,不要在dao层去配置!
- service上添加@TsTransactional注解表示需要事务,使用的是mybatis,由框架自动分配事务管理器,@TsTransactional(value="mydsTransactionManager")手动指定事务管理器,此时框架将不会自动分配
- service上添加@NoTransactional注解明确表示不需要事务,框架将不对此service做任何处理
- 所有service均应该配置@TsTransactional或者@NoTransactional注解,否则项目启动将报错
- @TsTransactional(ormType = OrmTypeEnum.IBATIS)表示使用ibatis(不写默认表示使用mybatis)
# 框架默认包路径规则(针对于多数据源的情况)
- service上配置了@TsTransactional注解后,框架会自动解析,并自动分配事务管理器,规则为包路径上要包括数据源的beanName,例如:com._2kcd.ts.module.ds1.service.impl,包路径中包括了“ds1”这个数据源,则自动分配“ds1TransactionManager”,相当于@TsTransactional("ds1TransactionManager")
- 如果包路径上没有对应数据源,则分配默认事务管理器“tsdsTransactionManager”
- 分配规则可自定义实现,需要实现DataSourceRegisterRule,添加注解@Component(value="projectDataSourceRegisterRule"),但原则上必须满足“事务管理器使用说明”的第一条。
# 事务检测
- 项目启动时,会自动检测事务,对于配置了@Service注解的,必须明确指定@TsTransactional,@NoTransactional两注解中的一个,否则报错
- 项目运行过程中,会对每次数据库操作做事务检测,当发现readOnly=false时,时select操作,则提醒这个地方需要优化
- 事务配置已经开启“强制读”操作,即当readOnly=true时,有增删改操作,直接报错。
# 事务测试
# 同一个数据源
# AService有事务,BService无事务
AService
public void updateUser(Long userid, String name) {
TaUser user = new TaUser();
user.setUserid(userid);
user.setName(name);
AMapper.updateByPrimaryKeySelective(user);
BService.updateUser(new Short("2"),"ds111sssss");
}
此种方式,如果BService报错,AService,BService都会回滚
AService
public void updateUser(Long userid, String name) {
TaUser user = new TaUser();
user.setUserid(userid);
user.setName(name);
AMapper.updateByPrimaryKeySelective(user);
BService.updateUser(new Short("2"),"ds111sssss");
String a = "1";
if(a.length() < 2) {
throw new AppException("ssssssssssssssssssssssss");
}
}
此种方式,BService完成调用后,AService中报错,AService,BService都会回滚!
# AService无事务,BService有事务
AService
public void updateUser(Long userid, String name) {
TaUser user = new TaUser();
user.setUserid(userid);
user.setName(name);
AMapper.updateByPrimaryKeySelective(user);
BService.updateUser(new Short("2"),"ds111sssss");
String a = "1";
if(a.length() < 2) {
throw new AppException("ssssssssssssssssssssssss");
}
}
此种方式,BService完成调用后,AService中报错,AService,BService都不会回滚!
AService
public void updateUser(Long userid, String name) {
TaUser user = new TaUser();
user.setUserid(userid);
user.setName(name);
AMapper.updateByPrimaryKeySelective(user);
BService.updateUser(new Short("2"),"ds111sssss");
}
此种方式,如果BService报错,AService不会回滚,BService会回滚
# AService有事务,BService有事务
AService
public void updateUser(Long userid, String name) {
TaUser user = new TaUser();
user.setUserid(userid);
user.setName(name);
AMapper.updateByPrimaryKeySelective(user);
BService.updateUser(new Short("2"),"ds111sssss");
}
此种方式,如果BService报错,AService,BService都会回滚
AService
public void updateUser(Long userid, String name) {
TaUser user = new TaUser();
user.setUserid(userid);
user.setName(name);
AMapper.updateByPrimaryKeySelective(user);
BService.updateUser(new Short("2"),"ds111sssss");
String a = "1";
if(a.length() < 2) {
throw new AppException("ssssssssssssssssssssssss");
}
}
此种方式,BService完成调用后,AService中报错,AService,BService都会回滚!
# 不同数据源
# AService有事务,BService无事务
AService
public void updateUser(Long userid, String name) {
TaUser user = new TaUser();
user.setUserid(userid);
user.setName(name);
AMapper.updateByPrimaryKeySelective(user);
BService.updateUser(new Short("2"),"ds111sssss");
}
此种方式,如果BService报错,BService不会回滚,而AService会回滚
AService
public void updateUser(Long userid, String name) {
TaUser user = new TaUser();
user.setUserid(userid);
user.setName(name);
AMapper.updateByPrimaryKeySelective(user);
BService.updateUser(new Short("2"),"ds111sssss");
String a = "1";
if(a.length() < 2) {
throw new AppException("ssssssssssssssssssssssss");
}
}
此种方式,BService完成调用后,AService中报错,则AService会回滚,BService不会回滚!
# AService有事务,BService有事务
AService
public void updateUser(Long userid, String name) {
TaUser user = new TaUser();
user.setUserid(userid);
user.setName(name);
AMapper.updateByPrimaryKeySelective(user);
BService.updateUser(new Short("2"),"ds111sssss");
}
此种方式,如果BService报错,则AService会回滚
AService
public void updateUser(Long userid, String name) {
TaUser user = new TaUser();
user.setUserid(userid);
user.setName(name);
AMapper.updateByPrimaryKeySelective(user);
BService.updateUser(new Short("2"),"ds111sssss");
String a = "1";
if(a.length() < 2) {
throw new AppException("ssssssssssssssssssssssss");
}
}
此种方式,BService完成调用后,AService中报错,则AService会回滚,BService不会回滚!