<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
一個本地環境的MySQL資料庫,資料庫mydb,建立表t_user
CREATE TABLE `t_user` ( `c_id` varchar(20) NOT NULL, `c_username` varchar(20) DEFAULT NULL, `c_password` varchar(20) DEFAULT NULL, `c_gender` tinyint(2) DEFAULT NULL, PRIMARY KEY (`c_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `mydb`.`t_user`(`c_id`, `c_username`, `c_password`, `c_gender`) VALUES ('1', '思思', '123', 1);
一個雲伺服器的MySQL資料庫,建立資料庫book_db,建立表t_userinfo。
CREATE TABLE `t_user_info` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '使用者id', `user_name` varchar(50) DEFAULT NULL COMMENT '使用者名稱', `password` varchar(255) DEFAULT NULL COMMENT '登入密碼', `areaObj` varchar(255) DEFAULT NULL COMMENT '所在學院', `name` varchar(20) DEFAULT NULL COMMENT '姓名', `sex` tinyint(255) DEFAULT NULL COMMENT '性別', `user_photo` varchar(255) DEFAULT NULL COMMENT '學生照片', `birthday` varchar(20) DEFAULT NULL COMMENT '出生日期', `telephone` varchar(20) DEFAULT NULL COMMENT '聯絡電話', `address` varchar(255) DEFAULT NULL COMMENT '家庭地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO `book_db`.`t_user_info`(`id`, `user_name`, `password`, `areaObj`, `name`, `sex`, `user_photo`, `birthday`, `telephone`, `address`) VALUES (1, '張三', '123', '哈爾濱', '張三散', 1, '123', '02-16', '15756892458', '黑龍江省哈爾濱市');
建立資料庫chatroom,建立表admin。
CREATE TABLE `admin` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) NOT NULL COMMENT '登入賬號', `nickname` varchar(20) NOT NULL COMMENT '暱稱', `password` varchar(255) NOT NULL COMMENT '密碼', `user_profile` varchar(255) DEFAULT NULL COMMENT '管理員頭像', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; INSERT INTO `chatroom`.`admin`(`id`, `username`, `nickname`, `password`, `user_profile`) VALUES (1, 'admin', '系統管理員', '$2a$10$PyloUEVGuO0fUZdfeIaROOTluRmccl.Scifa8S7Os0Wt.s4bDkb', 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1784117537,3335593911&fm=26&gp=0.jpg');
建立SpringBoot專案,整合MyBatis-Plus。pom.xml引入的依賴:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--mybatis plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.9</version> </dependency> </dependencies>
設定讀取resource資料夾下的mapper檔案
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
Spring Boot提供了AbstractRoutingDataSource 根據使用者定義的規則選擇要使用的資料來源,這樣我們可以在每次資料庫操作前設定使用的資料來源,實現可動態路由的資料來源。它的抽象方法determineCurrentLookupKey() 決定使用哪個資料來源。
getConnection()獲取資料庫連線,根據查詢lookup key鍵對不同目標資料來源的呼叫,通常是通過(但不一定)某些執行緒繫結的事物上下文來實現。通過這我們知道可以實現:資料來源的動態切換,在程式執行時,把資料來源動態織入到程式中,靈活得進行資料來源切換,從而可以不依賴中介軟體,實現讀寫分離功能。
AbstractRoutingDataSource實現邏輯:
呼叫AbstractRoutingDataSource的getConnection()的方法的時候,先呼叫determineTargetDataSource()方法返回DataSource在進行getConnection()。
determineTargetDataSource()方法通過呼叫determineCurrentLookupKey() 方法返回的lookupKey決定使用哪個資料來源。
定義資料來源列舉類DataSourceTypeEnum
public enum DataSourceTypeEnum { /** * chatroom */ CHATROOM("chatroom"), /** * book_db */ BOOK_DB("book_db"), /** * mydb */ MY_DB("mydb"); private final String name; DataSourceTypeEnum(String name) { this.name = name; } public String getName() { return name; } }
定義一個動態多資料來源類DynamicDataSource用於管理不同執行緒間多個資料來源的選擇和切換,擴充套件 Spring 提供的 AbstractRoutingDataSource 抽象類,重寫 determineCurrentLookupKey 方法,其中的determineCurrentLookupKey() 方法用於決定使用哪個資料來源。
public class DynamicDataSource extends AbstractRoutingDataSource { /** * ThreadLocal 用於提供執行緒區域性變數,在多執行緒環境可以保證各個執行緒裡的變數獨立於其它執行緒裡的變數。 * 也就是說 ThreadLocal 可以為每個執行緒建立一個【單獨的變數副本】,相當於執行緒的 private static 型別變數。 */ private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); /** * 決定使用哪個資料來源之前需要把多個資料來源的資訊以及預設資料來源資訊設定好 * * @param defaultTargetDataSource 預設資料來源 * @param targetDataSources 目標資料來源 */ public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } /** * determineCurrentLookupKey決定使用哪個資料庫 * @return */ @Override protected Object determineCurrentLookupKey() { return getDataSource(); } public static void setDataSource(String dataSource) { CONTEXT_HOLDER.set(dataSource); } public static String getDataSource() { return CONTEXT_HOLDER.get(); } public static void clearDataSource() { CONTEXT_HOLDER.remove(); } }
DynamicDataSourceConfig類作為設定類,讀取組態檔的三個資料來源的設定,建立對應DataSource型別的Bean。
@Configuration public class DynamicDataSourceConfig { @Bean(name="chatroom") @ConfigurationProperties("spring.datasource.druid.first") public DataSource dataSource1(){ return DruidDataSourceBuilder.create().build(); } @Bean(name ="book_db") @ConfigurationProperties("spring.datasource.druid.second") public DataSource dataSource2(){ return DruidDataSourceBuilder.create().build(); } @Bean(name="mydb") @ConfigurationProperties("spring.datasource.druid.third") public DataSource dataSource3(){ return DruidDataSourceBuilder.create().build(); } @Bean(name="dynamicDataSource") @Primary public DynamicDataSource dataSource() { Map<Object, Object> targetDataSources = new HashMap<>(5); targetDataSources.put(DataSourceTypeEnum.CHATROOM.getName(), dataSource1()); targetDataSources.put(DataSourceTypeEnum.BOOK_DB.getName(), dataSource2()); targetDataSources.put(DataSourceTypeEnum.MY_DB.getName(), dataSource3()); return new DynamicDataSource(dataSource1(), targetDataSources); } }
自定義註解@SpecifyDataSource用於在Service層方法上標記要使用哪個資料來源。這裡定義預設使用資料來源 DataSourceType.CHATROOM。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SpecifyDataSource { /** * @return */ DataSourceTypeEnum value() default DataSourceTypeEnum.CHATROOM; }
定義資料來源介面類DataSourceAspect,用於實現有SpecifyDataSource註解標註的方法前切換註解指定的資料來源。
@Aspect @Component @Order(value = 1) public class DataSourceAspect { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Pointcut("@annotation(top.javahai.datasource.annotation.SpecifyDataSource)") public void dataSourcePointCut() { } @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); SpecifyDataSource ds = method.getAnnotation(SpecifyDataSource.class); if (ds == null) { DynamicDataSource.setDataSource(DataSourceType.CHATROOM.getName()); logger.info("set datasource is " + DataSourceType.CHATROOM); } else { DynamicDataSource.setDataSource(ds.value().getName()); logger.info("set datasource is " + ds.value().getName()); } try { return point.proceed(); } finally { DynamicDataSource.clearDataSource(); logger.info("clean datasource"); } } }
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver # 資料來源1 spring.datasource.druid.first.url=jdbc:mysql://158.156.444.68:3306/chatroom?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8 spring.datasource.druid.first.username=root spring.datasource.druid.first.password=123456 # 資料來源2 spring.datasource.druid.second.url=jdbc:mysql://158.156.444.68:3306/book_db?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8 spring.datasource.druid.second.username=root spring.datasource.druid.second.password=123456 #資料來源3 spring.datasource.druid.third.url=jdbc:mysql:///mydb?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8 spring.datasource.druid.third.username=root spring.datasource.druid.third.password=123456 mybatis-plus.mapper-locations=classpath:mapper/*.xml #輸出sql執行紀錄檔 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
建立實體類Admin
public class Admin implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Integer id; /** * 登入賬號 */ private String username; /** * 暱稱 */ private String nickname; /** * 密碼 */ private String password; /** * 管理員頭像 */ private String userProfile; //省略getter/setter方法
建立實體類TUser
public class TUser implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "c_id", type = IdType.AUTO) private Integer cId; private String cUsername; private String cPassword; private Integer cGender; }
建立實體類TUserinfo
@TableName(value = "t_user_info") public class TUserinfo implements Serializable { private static final long serialVersionUID = 1L; /** * user_name */ private String userName; /** * 登入密碼 */ private String password; /** * 所在學院 */ @TableField("areaObj") private String areaObj; /** * 姓名 */ private String name; /** * 性別 */ private Integer sex; /** * 學生照片 */ private String userPhoto; /** * 出生日期 */ private String birthday; /** * 聯絡電話 */ private String telephone; /** * 家庭地址 */ private String address; }
建立UserVO用於測試
public class UserVO { private List<Admin> adminList; private List<TUserinfo> tUserinfos; private List<TUser> tUsers; }
@Service public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService { public List<Admin> getAll(){ return this.list(null); } }
@Service public class TUserinfoServiceImpl extends ServiceImpl<TUserinfoMapper, TUserinfo> implements ITUserinfoService { @SpecifyDataSource(value = DataSourceTypeEnum.BOOK_DB) public List<TUserinfo> selectAll(){ return this.list(null); } }
@Service public class TUserServiceImpl extends ServiceImpl<TUserMapper, TUser> implements ITUserService { @SpecifyDataSource(value = DataSourceTypeEnum.MY_DB) public List<TUser> selectAll(){ return this.list(null); } }
public interface AdminMapper extends BaseMapper<Admin> { } public interface TUserinfoMapper extends BaseMapper<TUserinfo> { } public interface TUserMapper extends BaseMapper<TUser> { }
建立介面/test/list用於測試
@RestController @RequestMapping("/test") public class TestController { @Autowired private AdminServiceImpl adminService; @Autowired private TUserinfoServiceImpl userinfoService; @Autowired private TUserServiceImpl userService; @GetMapping("/list") public UserVO list(){ List<Admin> adminList= adminService.getAll(); List<TUserinfo> tUserinfos = userinfoService.selectAll(); List<TUser> tUsers = userService.selectAll(); UserVO userVO = new UserVO(); userVO.setAdminList(adminList); userVO.settUserinfos(tUserinfos); userVO.settUsers(tUsers); return userVO; } }
瀏覽器請求/test/list
檢視控制檯輸出,檢視資料來源的切換紀錄檔
完整Demo程式碼地址:https://github.com/JustCoding-Hai/learn-everyday/tree/master/learn-multi_data_source
到此這篇關於SpringBoot多資料來源設定並通過註解實現動態切換資料來源的文章就介紹到這了,更多相關SpringBoot 動態切換資料來源內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45