欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

@DS+@Transactional注解切換數(shù)據(jù)源失效問題及解決

 更新時間:2025年05月12日 16:43:50   作者:duck不必鴨鴨  
這篇文章主要介紹了@DS+@Transactional注解切換數(shù)據(jù)源失效問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

背景

項目中使用了MySQL數(shù)據(jù)庫,并按照功能模塊采用了分庫的策略。因此,一個業(yè)務邏輯類中可能涉及多個MySQL數(shù)據(jù)庫的操作。

我們項目中是采用@DS(“xxx”)來實現(xiàn)數(shù)據(jù)源切換。

  • 當注解添加到類上,意味著此類里的方法都使用此數(shù)據(jù)源;
  • 當注解添加到方法上時,意味著此方法上使用的數(shù)據(jù)源優(yōu)先級高于其他一切配置;

問題分析

代碼

  • 依賴
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.1</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.3.1</version>
</dependency>
  • yml配置
spring:
  datasource:
    dynamic:
      primary: master #設置默認的數(shù)據(jù)源或者數(shù)據(jù)源組,默認值即為master
      strict: false #嚴格匹配數(shù)據(jù)源,默認false. true未匹配到指定數(shù)據(jù)源時拋異常,false使用默認數(shù)據(jù)源
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/demo_01?useSSL=false&autoReconnect=true&characterEncoding=utf8
          username: root
          password: xxx
          driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0開始支持SPI可省略此配置
        slave:
          url: jdbc:mysql://172.23.168.70:3306/dynamic?useSSL=false&autoReconnect=true&characterEncoding=utf8
          username: root
          password: xxx
          driver-class-name: com.mysql.cj.jdbc.Driver
  • 對象實體
/**
 * @author itender
 * @date 2023/4/28 11:01
 * @desc
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_dynamic_template")
public class DynamicTemplateEntity {

    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 語言
     */
    private String language;

    /**
     * 語言編碼
     */
    @TableField("language_code")
    private String languageCode;

    /**
     * 創(chuàng)建時間
     */
    @TableField("created_time")
    private Date createdTime;

    /**
     * 創(chuàng)建人
     */
    @TableField("created_by")
    private Integer createdBy;

    /**
     * 創(chuàng)建人名稱
   */
    @TableField("created_by_name")
    private String createdByName;
}
/**
 * @author itender
 * @date 2023/4/28 10:57
 * @desc
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@TableName("t_user")
public class UserEntity {

    /**
     * 主鍵id
     */
    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 用戶名稱
     */
    private String username;
}
  • controller代碼
/**
 * @author itender
 * @date 2023/4/28 10:34
 * @desc
 */
@RestController
@RequestMapping("template")
public class DynamicTemplateController {

    private final DynamicTemplateService dynamicTemplateService;

    @Autowired
    public DynamicTemplateController(DynamicTemplateService dynamicTemplateService) {
        this.dynamicTemplateService = dynamicTemplateService;
    }

    @GetMapping
    public List<DynamicTemplateEntity> list() {
        return dynamicTemplateService.list();
    }

    @PostMapping
    public Integer add(@RequestBody DynamicTemplateEntity template) {
        return dynamicTemplateService.add(template);
    }
}
  • service
/**
 * @author itender
 * @date 2023/4/28 10:36
 * @desc
 */
public interface DynamicTemplateService {

    /**
     * 查詢模板集合
     *
     * @return
     */
    List<DynamicTemplateEntity> list();

    /**
     * 添加模板
     *
     * @param template
     * @return
     */
    Integer add(DynamicTemplateEntity template);
}
  • mapper
/**
 * @author itender
 * @date 2023/4/28 11:09
 * @desc
 */
@DS("slave")
@Mapper
@Repository
public interface DynamicTemplateMapper extends BaseMapper<DynamicTemplateEntity> {
}
/**
 * @author itender
 * @date 2023/4/28 11:08
 * @desc
 */
@Mapper
@Repository
@DS("master")
public interface UserMapper extends BaseMapper<UserEntity> {
}
  • 業(yè)務代碼
/**
 * @author itender
 * @date 2023/4/28 11:15
 * @desc
 */
@Service
public class DynamicTemplateServiceImpl implements DynamicTemplateService {

    private final DynamicTemplateMapper dynamicTemplateMapper;

    private final UserMapper userMapper;

    private final UserService userService;

    @Autowired
    public DynamicTemplateServiceImpl(DynamicTemplateMapper dynamicTemplateMapper, UserMapper userMapper, UserService userService) {
        this.dynamicTemplateMapper = dynamicTemplateMapper;
        this.userMapper = userMapper;
        this.userService = userService;
    }

    @Override
    public List<DynamicTemplateEntity> list() {
        List<DynamicTemplateEntity> templateList = dynamicTemplateMapper.selectList(new QueryWrapper<>());
        if (CollectionUtils.isEmpty(templateList)) {
            return Lists.newArrayList();
        }
        List<UserEntity> userList = userMapper.selectList(new QueryWrapper<>());
        if (CollectionUtils.isEmpty(userList)) {
            return templateList;
        }
        Map<Integer, String> userMap = userList.stream().collect(Collectors.toMap(UserEntity::getId, UserEntity::getUsername, (key1, key2) -> key1));
        templateList.forEach(template -> template.setCreatedByName(userMap.get(template.getCreatedBy())));
        return templateList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Integer add(DynamicTemplateEntity template) {
        List<UserEntity> userList = userMapper.selectList(new QueryWrapper<>());
        if (CollectionUtils.isEmpty(userList)) {
            template.setCreatedByName("");
        }
        Map<Integer, String> userMap = userList.stream().collect(Collectors.toMap(UserEntity::getId, UserEntity::getUsername, (key1, key2) -> key1));
        template.setCreatedByName(userMap.get(template.getCreatedBy()));
        template.setCreatedTime(new Date());
        dynamicTemplateMapper.insert(template);
        return template.getId();
    }
}

測試

  • 當方法沒有@Transactional注解時,可以正常切換數(shù)據(jù)源
[
    {
        "id": 1,
        "language": "中文",
        "languageCode": "chinese",
        "createdTime": "2023-04-27T18:56:25.000+00:00",
        "createdBy": 1,
        "createdByName": "itender"
    }
]

可以正常切換數(shù)據(jù)源。

  • 當方法有@Transactional注解時,切換數(shù)據(jù)源失敗
### Error updating database.  Cause: java.sql.SQLSyntaxErrorException: Table 'demo_01.t_dynamic_template' doesn't exist
### The error may exist in com/itender/threadpool/mapper/DynamicTemplateMapper.java (best guess)
### The error may involve com.itender.threadpool.mapper.DynamicTemplateMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO t_dynamic_template  ( language, language_code, created_time, created_by, created_by_name )  VALUES  ( ?, ?, ?, ?, ? )
### Cause: java.sql.SQLSyntaxErrorException: Table 'demo_01.t_dynamic_template' doesn't exist
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: Table 'demo_01.t_dynamic_template' doesn't exist] with root cause
java.sql.SQLSyntaxErrorException: Table 'demo_01.t_dynamic_template' doesn't exist

分析

  • spring 的@Transactional聲明式事務管理時通過動態(tài)代理實現(xiàn)的。
  • @DS注解加到mapper接口、service接口、service方法里都不生效,獲取的還是默認的主數(shù)據(jù)源。猜測是由于spring的aop切面機制導致攔截不到@DS注解,進而不能切換數(shù)據(jù)源,正確的做法是添加到service實現(xiàn)類或者實現(xiàn)類里具體的方法上。
  • 在事務方法內(nèi)調(diào)用@DS注解的方法,@DS注解同樣不生效,原因是spring只能攔截到最外層方法的@Transactional注解,此時加載該事務的數(shù)據(jù)源,在事務方法內(nèi)即使調(diào)用了@DS注解的方法,獲取的是外層事務的數(shù)據(jù)源,導致@DS失效。
  • 在同一個實現(xiàn)類中,一個非DS注解的常規(guī)方法里調(diào)用@DS注解的方法,同樣存在@DS失效的情況,原因同2,是由spring的aop機制導致的,如果確有這種業(yè)務需要,可以將該DS注解方法定義在不同的類中,通過bean注入的方式調(diào)用,就不會出現(xiàn)這個問題。

解決方案

  • 把查詢user的邏輯放到另外一個單獨的業(yè)務邏輯類里面
/**
 * @author itender
 * @date 2023/4/28 14:25
 * @desc
 */
public interface UserService {

    /**
     * 查詢用戶集合
     *
     * @return
     */
    List<UserEntity> list();
}
/**
 * @author itender
 * @date 2023/4/28 14:27
 * @desc
 */
@Service
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;

    @Autowired
    public UserServiceImpl(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @DS("master")
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    @Override
    public List<UserEntity> list() {
        return userMapper.selectList(new QueryWrapper<>());
    }
}
  • 修改template業(yè)務類
@DS("slave")
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
@Override
public Integer add(DynamicTemplateEntity template) {
    // List<UserEntity> userList = userMapper.selectList(new QueryWrapper<>());
    List<UserEntity> userList = userService.list();
    if (CollectionUtils.isEmpty(userList)) {
        template.setCreatedByName("");
    }
    Map<Integer, String> userMap = userList.stream().collect(Collectors.toMap(UserEntity::getId, UserEntity::getUsername, (key1, key2) -> key1));
    template.setCreatedByName(userMap.get(template.getCreatedBy()));
    template.setCreatedTime(new Date());
    dynamicTemplateMapper.insert(template);
    return template.getId();
}

測試成功插入一條數(shù)據(jù)。

總結

spring 的@Transactional聲明式事務管理時通過動態(tài)代理實現(xiàn)的。

@DS注解加到mapper接口、service接口、service方法里都不生效,獲取的還是默認的主數(shù)據(jù)源。猜測是由于spring的aop切面機制導致攔截不到@DS注解,進而不能切換數(shù)據(jù)源,正確的做法是添加到service實現(xiàn)類或者實現(xiàn)類里具體的方法上。

在事務方法內(nèi)調(diào)用@DS注解的方法,@DS注解同樣不生效,原因是spring只能攔截到最外層方法的@Transactional注解,此時加載該事務的數(shù)據(jù)源,在事務方法內(nèi)即使調(diào)用了@DS注解的方法,獲取的是外層事務的數(shù)據(jù)源,導致@DS失效。

在同一個實現(xiàn)類中,一個非DS注解的常規(guī)方法里調(diào)用@DS注解的方法,同樣存在@DS失效的情況,原因同2,是由spring的aop機制導致的,如果確有這種業(yè)務需要,可以將該DS注解方法定義在不同的類中,通過bean注入的方式調(diào)用,就不會出現(xiàn)這個問題。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • 如何配置feign全局log

    如何配置feign全局log

    這篇文章主要介紹了如何配置feign全局log,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • MyBatis一級緩存與二級緩存原理與作用分析

    MyBatis一級緩存與二級緩存原理與作用分析

    mybatis-plus是一個Mybatis的增強工具,在Mybatis的基礎上只做增強不做改變,為簡化開發(fā)、提高效率而生,這篇文章帶你了解Mybatis的一級和二級緩存
    2022-12-12
  • 實例講解Java編程中數(shù)組反射的使用方法

    實例講解Java編程中數(shù)組反射的使用方法

    這篇文章主要介紹了Java編程中數(shù)組反射的使用方法,通過編寫數(shù)組反射工具類可以重用許多基礎代碼,減少對類型的判斷過程,需要的朋友可以參考下
    2016-04-04
  • java實現(xiàn)文件保存到本地的方法

    java實現(xiàn)文件保存到本地的方法

    本篇文章主要介紹了java實現(xiàn)文件保存到本地的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • SpringBoot入坑筆記之spring-boot-starter-web 配置文件的使用

    SpringBoot入坑筆記之spring-boot-starter-web 配置文件的使用

    本篇向小伙伴介紹springboot配置文件的配置,已經(jīng)全局配置參數(shù)如何使用的。需要的朋友跟隨腳本之家小編一起學習吧
    2018-01-01
  • SpringBoot讀取Nacos上配置文件的步驟詳解

    SpringBoot讀取Nacos上配置文件的步驟詳解

    在 Spring Boot 應用程序中,可以使用 Spring Cloud Nacos 來實現(xiàn)從 Nacos 服務注冊中心和配置中心讀取配置信息,本文介紹如何在 Spring Boot 中讀取 Nacos 上的配置文件的步驟,需要的朋友可以參考下
    2024-03-03
  • MyBatis-Plus中自動填充功能的用法示例詳解

    MyBatis-Plus中自動填充功能的用法示例詳解

    有些時候我們可能會有這樣的需求,插入或者更新數(shù)據(jù)時,希望有些字段可以自動填充數(shù)據(jù),比如密碼、version、注冊時默認的用戶角色等,在MP中提供了這樣的功能,可以實現(xiàn)自動填充功能,需要的朋友可以參考下
    2022-12-12
  • 基于mybatis中test條件中單引號雙引號的問題

    基于mybatis中test條件中單引號雙引號的問題

    這篇文章主要介紹了基于mybatis中test條件中單引號雙引號的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java list.remove( )方法注意事項

    Java list.remove( )方法注意事項

    這篇文章主要介紹了Java list.remove( )方法注意事項,非常簡單易懂,需要的朋友可以參考下
    2018-08-08
  • SpringBoot使用CORS實現(xiàn)無縫跨域的方法實現(xiàn)

    SpringBoot使用CORS實現(xiàn)無縫跨域的方法實現(xiàn)

    CORS 是一種在服務端設置響應頭部信息的機制,允許特定的源進行跨域訪問,本文主要介紹了SpringBoot使用CORS實現(xiàn)無縫跨域的方法實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2023-10-10

最新評論