<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot Jpa 多數(shù)據(jù)源動態(tài)切換

          共 1349字,需瀏覽 3分鐘

           ·

          2022-01-26 13:29

          在大型應用程序中,配置主從數(shù)據(jù)庫并使用讀寫分離是常見的設(shè)計模式。常用的實現(xiàn)方式是使用數(shù)據(jù)庫中間件,此文介紹如何通過編寫代碼的方式實現(xiàn)多數(shù)據(jù)源的配置和動態(tài)切換。核心是使用Spring 內(nèi)置的?AbstractRoutingDataSource?這個抽象類,它可以把多個數(shù)據(jù)源配置成一個Map,然后,根據(jù)不同的key返回不同的數(shù)據(jù)源。

          環(huán)境介紹

          • SpringBoot 1.5.10.RELEASE

          • MySQL 5.7

          數(shù)據(jù)源配置

          首先在?application.yml?里配置兩個數(shù)據(jù)源:

          spring:
          ??datasource:???#多數(shù)據(jù)源配置
          ????master:
          ??????url:?jdbc:mysql://localhost:3307/testdb?useUnicode=true&characterEncoding=utf8&useSSL=false
          ??????username:?root
          ??????password:?123456
          ??????driver-class-name:?com.mysql.jdbc.Driver
          ????slave:
          ??????url:?jdbc:mysql://localhost:3308/testdb?useUnicode=true&characterEncoding=utf8&useSSL=false
          ??????username:?root
          ??????password:?123456
          ??????driver-class-name:?com.mysql.jdbc.Driver
          ????type:?com.alibaba.druid.pool.DruidDataSource

          ??jpa:
          ????show-sql:?true
          #????hibernate:
          #??????naming:?這個屬性不知道為什么無法自動獲取到,需要在代碼賦值
          #????????physical-strategy:?org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
          server:
          ??port:?8080
          ??context-path:?/imooc

          初始化數(shù)據(jù)源

          編寫數(shù)據(jù)源配置類,初始化數(shù)據(jù)源,并把兩個物理數(shù)據(jù)源封裝成一個AbstractRoutingDataSource

          @Configuration
          public?class?DataSourceConfiguration?{
          ????private?final?static?String?MASTER_DATASOURCE_KEY?=?"masterDataSource";
          ????private?final?static?String?SLAVE_DATASOURCE_KEY?=?"slaveDataSource";

          ????@Value("${spring.datasource.type}")
          ????private?Class?dataSourceType;

          ????@Primary
          ????@Bean(value?=?MASTER_DATASOURCE_KEY)
          ????@Qualifier(MASTER_DATASOURCE_KEY)
          ????@ConfigurationProperties(prefix?=?"spring.datasource.master")
          ????public?DataSource?masterDataSource()?{
          ????????log.info("create?master?datasource...");
          ????????return?DataSourceBuilder.create().type(dataSourceType).build();
          ????}

          ????@Bean(value?=?SLAVE_DATASOURCE_KEY)
          ????@Qualifier(SLAVE_DATASOURCE_KEY)
          ????@ConfigurationProperties(prefix?=?"spring.datasource.slave")
          ????public?DataSource?slaveDataSource()?{
          ????????log.info("create?slave?datasource...");
          ????????return?DataSourceBuilder.create().type(dataSourceType).build();
          ????}

          ????@Bean(name?=?"routingDataSource")
          ????public?AbstractRoutingDataSource?routingDataSource(@Qualifier("masterDataSource")?DataSource?masterDataSource,
          ????????????@Qualifier("slaveDataSource")?DataSource?slaveDataSource)?{
          ????????DynamicDataSourceRouter?proxy?=?new?DynamicDataSourceRouter();
          ????????Map?targetDataSources?=?new?HashMap<>(2);
          ????????targetDataSources.put("masterDataSource",?masterDataSource);
          ????????targetDataSources.put("slaveDataSource",?slaveDataSource);

          ????????proxy.setDefaultTargetDataSource(masterDataSource);
          ????????proxy.setTargetDataSources(targetDataSources);
          ????????return?proxy;
          ????}
          }

          注意需要把其中一個數(shù)據(jù)源使用@Primary?注解標明為主數(shù)據(jù)源,并且這個主數(shù)據(jù)源不能是AbstractRoutingDataSource類型的,必須是DataSource?類型的。

          編寫?JpaEntityManager?配置類

          使用多數(shù)據(jù)源后,需要手動對?Jpa?的?EntityManager?進行初始化和配置,不能使用默認的自動配置,不然的話并不能實際創(chuàng)建兩個不同的數(shù)據(jù)源。

          @Configuration
          @EnableConfigurationProperties(JpaProperties.class)
          @EnableJpaRepositories(value?=?"com.imooc.dao.repository")
          public?class?JpaEntityManager?{

          ????@Autowired
          ????private?JpaProperties?jpaProperties;

          ????@Resource(name?=?"routingDataSource")
          ????private?DataSource?routingDataSource;

          ????//@Primary
          ????@Bean(name?=?"entityManagerFactoryBean")
          ????public?LocalContainerEntityManagerFactoryBean?entityManagerFactoryBean(EntityManagerFactoryBuilder?builder)?{
          ????????//?不明白為什么這里獲取不到?application.yml?里的配置
          ????????Map?properties?=?jpaProperties.getProperties();
          ????????//要設(shè)置這個屬性,實現(xiàn)?CamelCase?->?UnderScore?的轉(zhuǎn)換
          ????????properties.put("hibernate.physical_naming_strategy",
          ????????????????"org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");

          ????????return?builder
          ????????????????.dataSource(routingDataSource)//關(guān)鍵:注入routingDataSource
          ????????????????.properties(properties)
          ????????????????.packages("com.imooc.entity")
          ????????????????.persistenceUnit("myPersistenceUnit")
          ????????????????.build();
          ????}

          ????@Primary
          ????@Bean(name?=?"entityManagerFactory")
          ????public?EntityManagerFactory?entityManagerFactory(EntityManagerFactoryBuilder?builder)?{
          ????????return?this.entityManagerFactoryBean(builder).getObject();
          ????}

          ????@Primary
          ????@Bean(name?=?"transactionManager")
          ????public?PlatformTransactionManager?transactionManager(EntityManagerFactoryBuilder?builder)?{
          ????????return?new?JpaTransactionManager(entityManagerFactory(builder));
          ????}
          }

          編寫動態(tài)保存數(shù)據(jù)源類型key的實現(xiàn)類

          使用?ThreadLocal?來動態(tài)設(shè)置和保存數(shù)據(jù)源類型的key

          public?class?DataSourceContextHolder?{
          ????private?static?final?ThreadLocal?holder?=?new?ThreadLocal<>();

          ????public?static?void?setDataSource(String?type)?{
          ????????holder.set(type);
          ????}

          ????public?static?String?getDataSource()?{
          ????????String?lookUpKey?=?holder.get();
          ????????return?lookUpKey?==?null???"masterDataSource"?:?lookUpKey;
          ????}

          ????public?static?void?clear()?{
          ????????holder.remove();
          ????}
          }

          實現(xiàn)AbstractRoutingDataSource

          編寫一個類繼承AbstractRoutingDataSource,并重寫?determineCurrentLookupKey?這個路由方法:

          public?class?DynamicDataSourceRouter?extends?AbstractRoutingDataSource?{
          ????@Override
          ????protected?Object?determineCurrentLookupKey()?{
          ????????return?DataSourceContextHolder.getDataSource();
          ????}
          }

          編寫切面實現(xiàn)動態(tài)切換

          日常工作中,通常是根據(jù)Service?層的方法簽名,區(qū)分讀寫操作,最便捷的方式是使用 AOP 進行攔截:

          @Slf4j
          @Aspect
          @Component
          public?class?DynamicDataSourceAspect?{

          ????@Pointcut("execution(*?com.imooc.service..*.*(..))")
          ????private?void?aspect()?{}

          ????@Around("aspect()")
          ????public?Object?around(ProceedingJoinPoint?joinPoint)?throws?Throwable?{
          ????????String?method?=?joinPoint.getSignature().getName();

          ????????if?(method.startsWith("find")?||?method.startsWith("select")?||?method.startsWith("query")?||?method
          ????????????????.startsWith("search"))?{
          ????????????DataSourceContextHolder.setDataSource("slaveDataSource");
          ????????????log.info("switch?to?slave?datasource...");
          ????????}?else?{
          ????????????DataSourceContextHolder.setDataSource("masterDataSource");
          ????????????log.info("switch?to?master?datasource...");
          ????????}

          ????????try?{
          ????????????return?joinPoint.proceed();
          ????????}finally?{
          ????????????log.info("清除?datasource?router...");
          ????????????DataSourceContextHolder.clear();
          ????????}
          ????}
          }

          總結(jié)

          至此,核心的代碼和細節(jié)已經(jīng)講解結(jié)束,其余的實體類、Repository接口、Service 方法、測試用例等,可以參考。

          https://github.com/linyongfu2013/springboot-multi-datasource.git
          //linyongfu2013.github.io/2018/03/08/springboot-jpa-dynamic-datasource/

          分享&在看


          瀏覽 57
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区无码免费 | 午夜欧美精品久久久久久久 | h在线网站| 国产毛片一区 | 免费黄色片视频网站 |