# 多数据源和事务使用文档

# 多数据源配置说明

在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支持只读事务,某些数据库不支持此属性,需要关闭

# 数据源事务说明

  1. 数据源和事务管理器是一一对应的,所有service都应该进行分类,一个service里面只能注入同一数据源的dao或者mapper,如果需要用到其他数据源,则应该以service的形式注入进去
  2. 所有事务均在service上去配置,不要在dao层去配置!
  3. @TsTransactional注解表示需要事务,使用的是mybatis,由框架自动分配事务管理器,@TsTransactional(value="mydsTransactionManager")手动指定事务管理器,此时框架将不会自动分配
  4. service上添加@NoTransactional注解明确表示不需要事务,框架将不对此service做任何处理
  5. 所有service均应该配置@TsTransactional或者@NoTransactional注解,否则项目启动将报错
  6. @TsTransactional(ormType = OrmTypeEnum.IBATIS)表示使用ibatis(不写默认表示使用mybatis)

# 框架默认包路径规则(针对于多数据源的情况)

  1. service上配置了@TsTransactional注解后,框架会自动解析,并自动分配事务管理器,规则为包路径上要包括数据源的beanName,例如:com.jinke.ts.module.ds1.service.impl,包路径中包括了“ds1”这个数据源,则自动分配“ds1TransactionManager”,相当于@TsTransactional("ds1TransactionManager")
  2. 如果包路径上没有对应数据源,则分配默认事务管理器“tsdsTransactionManager”
  3. 分配规则可自定义实现,需要实现DataSourceRegisterRule,添加注解@Component(value="projectDataSourceRegisterRule"),但原则上必须满足“事务管理器使用说明”的第一条。

# 事务检测

  1. 项目启动时,会自动检测事务,对于配置了@Service注解的,必须明确指定@TsTransactional,@NoTransactional两注解中的一个,否则报错
  2. 项目运行过程中,会对每次数据库操作做事务检测,当发现readOnly=false时,时select操作,则提醒这个地方需要优化
  3. 事务配置已经开启“强制读”操作,即当readOnly=true时,有增删改操作,直接报错。

# 事务管理器使用说明

  1. 数据源和事务管理器是一一对应的,所有service都应该进行分类,一个service里面只能注入同一数据源的dao或者mapper,如果需要用到其他数据源,则应该以service的形式注入进去
  2. 所有事务均在service上去配置,不要在dao层去配置!
  3. service上添加@TsTransactional注解表示需要事务,使用的是mybatis,由框架自动分配事务管理器,@TsTransactional(value="mydsTransactionManager")手动指定事务管理器,此时框架将不会自动分配
  4. service上添加@NoTransactional注解明确表示不需要事务,框架将不对此service做任何处理
  5. 所有service均应该配置@TsTransactional或者@NoTransactional注解,否则项目启动将报错
  6. @TsTransactional(ormType = OrmTypeEnum.IBATIS)表示使用ibatis(不写默认表示使用mybatis)

# 框架默认包路径规则(针对于多数据源的情况)

  1. service上配置了@TsTransactional注解后,框架会自动解析,并自动分配事务管理器,规则为包路径上要包括数据源的beanName,例如:com._2kcd.ts.module.ds1.service.impl,包路径中包括了“ds1”这个数据源,则自动分配“ds1TransactionManager”,相当于@TsTransactional("ds1TransactionManager")
  2. 如果包路径上没有对应数据源,则分配默认事务管理器“tsdsTransactionManager”
  3. 分配规则可自定义实现,需要实现DataSourceRegisterRule,添加注解@Component(value="projectDataSourceRegisterRule"),但原则上必须满足“事务管理器使用说明”的第一条。

# 事务检测

  1. 项目启动时,会自动检测事务,对于配置了@Service注解的,必须明确指定@TsTransactional,@NoTransactional两注解中的一个,否则报错
  2. 项目运行过程中,会对每次数据库操作做事务检测,当发现readOnly=false时,时select操作,则提醒这个地方需要优化
  3. 事务配置已经开启“强制读”操作,即当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不会回滚!

最后更新日期: 12/13/2021, 9:05:01 AM