精华内容
下载资源
问答
  • [Spring Boot] Spring Boot 多数据源动态切换[自定义注解&AOP] 目录[Spring Boot] Spring Boot 多数据源动态切换[自定义注解&AOP]基础框架准备动态数据源注册效果REFRENCES更 手机用户请横屏获取最佳阅读...

    [Spring Boot] Spring Boot 多数据源动态切换[自定义注解&AOP]

    手机用户请横屏获取最佳阅读体验,REFERENCES中是本文参考的链接,如需要链接和更多资源,可以关注其他博客发布地址。

    平台地址
    CSDNhttps://blog.csdn.net/sinat_28690417
    简书https://www.jianshu.com/u/3032cc862300
    个人博客http://xiazhaoyang.tech/

    开发环境描述

    ------------------------------------------------------------
    Gradle 4.7
    ------------------------------------------------------------
    
    Build time:   2018-04-18 09:09:12 UTC
    Revision:     b9a962bf70638332300e7f810689cb2febbd4a6c
    
    Groovy:       2.4.12
    Ant:          Apache Ant(TM) version 1.9.9 compiled on February 2 2017
    JVM:          1.8.0_171 (Oracle Corporation 25.171-b11)
    OS:           Mac OS X 10.13.5 x86_64
    
    

    依赖描述

    • spring-boot-starter-web:2.1.0.RELEASE
    • mybatis-spring-boot-starter:1.3.2
    • aspectjrt:1.9.2
    • aspectjweaver:1.9.2
    • mysql-connector-java:8.0.13

    基础框架准备

    • 准备两个数据源(mysql中schema和database是一个意思)并生成对应MapperModel

      CREATE SCHEMA db_capsule;

      CREATE SCHEMA db_flowable;

      分别在这两个数据库中创建一张表:db_capsule.tb_common_user_infodb_flowable.tb_common_account_info

      create table tb_common_user_info
      (
        user_id     bigint auto_increment
        comment '人员ID'
          primary key,
        age         int                                not null
        comment '年龄',
        name        varchar(128)                       not null
        comment '姓名',
        email       varchar(128)                       not null
        comment '邮箱',
        remark      varchar(1024)                      null
        comment '备注',
        is_delete   int default '0'                    null
        comment '表明数据是否已删除 0-未删除,1-已删除',
        create_time datetime default CURRENT_TIMESTAMP not null
        comment '创建时间'
      )
        comment '通用人员信息表';
      
      create table tb_common_account_info
      (
        account_id   bigint auto_increment
        comment '账号ID'
          primary key,
        account_name varchar(128)                       not null
        comment '登录名',
        user_id      bigint                             null
        comment '关联用户ID',
        remark       varchar(1024)                      null
        comment '备注',
        is_delete    int default '0'                    null
        comment '表明数据是否已删除 0-未删除,1-已删除',
        create_time  datetime default CURRENT_TIMESTAMP not null
        comment '创建时间'
      )
        comment '账号信息表';
      
    

    生成基础Mapper(代码生成插件即可)

    在这里插入图片描述

    在这里插入图片描述

    • yaml中配置默认数据源和自定义数据源
    spring:
      mvc:
       static-path-pattern: /**
      resources:
        static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/static/flowable-modeler
      # 默认主数据源
      datasource:
        type: com.zaxxer.hikari.HikariDataSource
        username: xx
        password: xxxx
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://x.x.x.x:3306/db_flowable?useUnicode=true&characterEncoding=utf-8
      jpa:
        hibernate:
          ddl-auto: update
        database: MYSQL
    
    # 自定义数据源
    custom:
      datasource:
        - key: capsule
          type: com.alibaba.druid.pool.DruidDataSource
          username: xxx
          password: xxxx
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://x.x.x.x:3306/db_capsule?useUnicode=true&characterEncoding=utf-8
    
    • 启动类开启AOP
    package com.example;
    
    import com.example.common.config.database.DynamicDataSourceRegister;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    import org.springframework.context.annotation.Import;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    /**
     * <p>
     *  - SpringBootApplication 启动类
     *  - ComponentScan 实例扫描
     *  - MapperScan Mybatis Dao 扫描
     *  - EnableTransactionManagement 开启事务
     *  - Import 启动前注入实例,动态切换数据源
     * </p>
     *
     * @author xiazhaoyang
     * @version v1.0.0
     * @date 2019/3/14 23:08
     * @modificationHistory=========================逻辑或功能性重大变更记录
     * @modify By: {修改人} 2019/3/14
     * @modify reason: {方法名}:{原因}
     * ...
     */
    @SpringBootApplication
    @ComponentScan(basePackages = {"com.example"})
    @Import({DynamicDataSourceRegister.class})
    @EnableAspectJAutoProxy(proxyTargetClass=true)
    public class CapsuleFlowableApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(CapsuleFlowableApplication.class, args);
        }
    }
    
    
    • 构建Service层测试代码

      AccountInfoServiceImpl

    package com.example.service.common.impl;
    import com.example.common.base.BaseServiceImpl;
    import com.example.common.base.RootMapper;
    import com.example.common.config.database.TargetDataSource;
    import com.example.core.common.dao.AccountInfoMapper;
    import com.example.core.common.model.AccountInfo;
    import com.example.core.common.model.AccountInfoExample;
    import com.example.service.common.AccountInfoService;
    import org.springframework.stereotype.Service;
    import lombok.extern.slf4j.Slf4j;
    import javax.annotation.Resource;
    import java.util.List;
    
    /**
     * <p>
     *
     * </p>
     *
     * @author xiazhaoyang
     * @version V1.0.0
     * @date 2019/03/24
     * @modificationHistory=========================逻辑或功能性重大变更记录
     * @modify By: {修改人}  2019/03/24
     * @modify reason: {方法名}:{原因}
     * ...
     */
    @Slf4j
    @Service
    public class AccountInfoServiceImpl extends BaseServiceImpl<AccountInfo,AccountInfoExample> implements AccountInfoService {
    
        @Resource
        private AccountInfoMapper accountInfoMapper;
    
    
        @Override
        public RootMapper<AccountInfo, AccountInfoExample> getMapper() {
            return accountInfoMapper;
        }
    
    
        @Override
        @TargetDataSource(name="dataSource")//此处为切换数据源的注解
        public List<AccountInfo> selectList() {
            return selectByExample(new AccountInfoExample());
        }
    }
    
    

    UserInfoServiceImpl

    package com.example.service.common.impl;
    import com.example.common.config.database.TargetDataSource;
    import com.example.core.common.dao.UserInfoMapper;
    import com.example.core.common.model.UserInfo;
    import com.example.core.common.model.UserInfoExample;
    import com.example.service.common.UserInfoService;
    import com.example.common.base.BaseServiceImpl;
    import com.example.common.base.RootMapper;
    import org.springframework.stereotype.Service;
    import lombok.extern.slf4j.Slf4j;
    import javax.annotation.Resource;
    import java.util.List;
    
    /**
     * <p>
     *
     * </p>
     *
     * @author xiazhaoyang
     * @version V1.0.0
     * @date 2019/03/24
     * @modificationHistory=========================逻辑或功能性重大变更记录
     * @modify By: {修改人}  2019/03/24
     * @modify reason: {方法名}:{原因}
     * ...
     */
    @Slf4j
    @Service
    public class UserInfoServiceImpl extends BaseServiceImpl<UserInfo,UserInfoExample> implements UserInfoService {
    
        @Resource
        private UserInfoMapper userInfoMapper;
    
        @Override
        public RootMapper<UserInfo, UserInfoExample> getMapper() {
            return userInfoMapper;
        }
    
    
        @Override
        @TargetDataSource(name="capsule")
        public List<UserInfo> selectList() {
            return selectByExample(new UserInfoExample());
        }
    
        @Override
        @TargetDataSource(name="capsule")//此处为切换数据源的注解
        public int insert(UserInfo userInfo) {
            return super.insert(userInfo);
        }
    }
    
    
    • 构建Controller层测试代码

    AccountInfoController

    package com.example.web.controller.common;
    
    import com.example.common.base.BaseController;
    import com.example.common.base.ResponseJson;
    import com.example.core.common.model.AccountInfo;
    import com.example.service.common.AccountInfoService;
    import lombok.extern.slf4j.Slf4j;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.*;
    
    import javax.annotation.Resource;
    import java.util.Date;
    
    /**
     * <p>
     *
     * </p>
     *
     * @author xiazhaoyang
     * @version V1.0.0
     * @date 2019/03/24
     * @modificationHistory=========================逻辑或功能性重大变更记录
     * @modify By: {修改人}  2019/03/24
     * @modify reason: {方法名}:{原因}
     * ...
     */
    @Slf4j
    @RestController
    @RequestMapping("/common/accountInfo/")
    public class AccountInfoController extends BaseController<AccountInfo>{
    
        @Resource
        AccountInfoService accountInfoService;
    
        @Override
        @RequestMapping(value="addition", method = RequestMethod.POST)
        public ResponseJson add(@RequestBody AccountInfo accountInfo) {
            accountInfoService.insert(AccountInfo.builder()
                    .accountId(1L)
                    .accountName("admin")
                    .createTime(new Date())
                    .userId(1L)
                    .remark("ADMIN REMARK")
                    .isDelete(0)
                    .build());
            return new ResponseJson();
        }
    
        @Override
        @RequestMapping(value="deletion", method = RequestMethod.POST)
        public ResponseJson delete(@RequestParam String id) {
    	    return new ResponseJson();
        }
    
        @Override
        @RequestMapping(value="update", method = RequestMethod.POST)
        public ResponseJson update(@RequestBody AccountInfo accountInfo) {
    	    return new ResponseJson();
        }
    
        @Override
        @RequestMapping(value="detail", method = RequestMethod.POST)
        public ResponseJson detail(@RequestParam String id) {
            return ResponseJson.OK(accountInfoService.selectList());
        }
    }
    
    

    UserInfoController

    package com.example.web.controller.common;
    import com.example.core.common.model.AccountInfo;
    import com.example.core.common.model.UserInfo;
    import com.example.service.common.UserInfoService;
    import com.github.pagehelper.PageHelper;
    import com.github.pagehelper.PageInfo;
    import lombok.extern.slf4j.Slf4j;
    import javax.annotation.Resource;
    import com.example.common.base.BaseController;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.*;
    import com.example.common.base.ResponseJson;
    
    import java.util.Date;
    
    /**
     * <p>
     *
     * </p>
     *
     * @author xiazhaoyang
     * @version V1.0.0
     * @date 2019/03/24
     * @modificationHistory=========================逻辑或功能性重大变更记录
     * @modify By: {修改人}  2019/03/24
     * @modify reason: {方法名}:{原因}
     * ...
     */
    @Slf4j
    @RestController
    @RequestMapping("/common/userInfo/")
    public class UserInfoController extends BaseController<UserInfo>{
    
        @Resource
        UserInfoService userInfoService;
    
        @Override
        @RequestMapping(value="addition", method = RequestMethod.POST)
        public ResponseJson add(@RequestBody UserInfo userInfo) {
            userInfoService.insert(UserInfo.builder()
                    .userId(1L)
                    .age(11)
                    .createTime(new Date())
                    .email("xxx@xx.com.cn")
                    .name("Yiyuery")
                    .remark("xxx")
                    .build());
            return new ResponseJson();
        }
    
        @Override
        @RequestMapping(value="deletion", method = RequestMethod.POST)
        public ResponseJson delete(@RequestParam String id) {
    	    return new ResponseJson();
        }
    
        @Override
        @RequestMapping(value="update", method = RequestMethod.POST)
        public ResponseJson update(@RequestBody UserInfo userInfo) {
    	    return new ResponseJson();
        }
    
        @Override
        @RequestMapping(value="detail", method = RequestMethod.POST)
        public ResponseJson detail(@RequestParam String id) {
            return ResponseJson.OK(userInfoService.selectList());
        }
    }
    
    

    动态数据源注册

    • 注解定义
      /*
       * @ProjectName: 编程学习
       * @Copyright:   2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
       * @address:     http://xiazhaoyang.tech
       * @date:        2019/3/23 17:05
       * @email:       xiazhaoyang@live.com
       * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
       */
      package com.example.common.config.database;
      
      import java.lang.annotation.*;
      
      /**
       * <p>
       *  在方法上使用,用于指定使用哪个数据源
       * </p>
       *
       * @author xiazhaoyang
       * @version v1.0.0
       * @date 2019/3/23 17:05
       * @modificationHistory=========================逻辑或功能性重大变更记录
       * @modify By: {修改人} 2019/3/23
       * @modify reason: {方法名}:{原因}
       * ...
       */
      @Target({ ElementType.METHOD, ElementType.TYPE })
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      public @interface TargetDataSource {
          String name();
      }
    
    • DynamicDataSourceRegister数据源实例bean注册
      /*
       * @ProjectName: 编程学习
       * @Copyright:   2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
       * @address:     http://xiazhaoyang.tech
       * @date:        2019/3/23 17:04
       * @email:       xiazhaoyang@live.com
       * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
       */
      package com.example.common.config.database;
      
      import lombok.extern.slf4j.Slf4j;
      import org.apache.commons.lang3.StringUtils;
      import org.springframework.beans.MutablePropertyValues;
      import org.springframework.beans.factory.support.BeanDefinitionRegistry;
      import org.springframework.beans.factory.support.GenericBeanDefinition;
      import org.springframework.boot.context.properties.bind.Bindable;
      import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
      import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
      import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
      import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
      import org.springframework.boot.jdbc.DataSourceBuilder;
      import org.springframework.context.EnvironmentAware;
      import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
      import org.springframework.core.env.Environment;
      import org.springframework.core.type.AnnotationMetadata;
      import org.springframework.boot.context.properties.bind.Binder;
      
      
      import javax.sql.DataSource;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      
      import static com.example.common.util.CapsuleStringUtil.requireNotSatisfy;
      
      /**
       * <p>
       *
       * </p>
       *
       * @author xiazhaoyang
       * @version v1.0.0
       * @date 2019/3/23 17:04
       * @modificationHistory=========================逻辑或功能性重大变更记录
       * @modify By: {修改人} 2019/3/23
       * @modify reason: {方法名}:{原因}
       * ...
       */
      @Slf4j
      public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
      
          /**
           * 参数绑定工具
           */
          private Binder binder;
          /**
           * 如配置文件中未指定数据源类型,使用该默认值
           */
          private static final Object DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";
      
          /**
           * 默认数据源
           */
          private DataSource defaultDataSource;
          /**
           * 自定义数据源
           */
          private Map<String, DataSource> customDataSources = new HashMap<>();
          /**
           * 数据源参数配置别名
           */
          private final static ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases(); //别名
          /**
           * 配置上下文(也可以理解为配置文件的获取工具)
           */
          private Environment env;
      
      
          static {
              //由于部分数据源配置不同,所以在此处添加别名,避免切换数据源出现某些参数无法注入的情况
              aliases.addAliases("url", "jdbc-url");
              aliases.addAliases("username", "user");
          }
      
          @Override
          public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
              Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
              // 将主数据源添加到更多数据源中
              targetDataSources.put("dataSource", defaultDataSource);
              DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
              // 添加更多数据源
              targetDataSources.putAll(customDataSources);
              DynamicDataSourceContextHolder.dataSourceIds.addAll(customDataSources.keySet());
              // 创建DynamicDataSource
              GenericBeanDefinition define = new GenericBeanDefinition(); //bean定义类
              define.setBeanClass(DynamicDataSource.class); //设置bean的类型,此处DynamicDataSource是继承AbstractRoutingDataSource的实现类
              MutablePropertyValues mpv = define.getPropertyValues(); //需要注入的参数,类似spring配置文件中的<property/>
              mpv.add("defaultTargetDataSource", defaultDataSource); //添加默认数据源,避免key不存在的情况没有数据源可用
              mpv.add("targetDataSources", targetDataSources); //添加其他数据源
              registry.registerBeanDefinition("datasource", define); //将该bean注册为datasource,不使用spring-boot自动生成的datasource
              log.info("Dynamic DataSource Registry");
          }
      
          /**
           * 创建DataSource
           *
           * @return
           * @author SHANHY
           * @create 2016年1月24日
           */
          private DataSource buildDataSource(Map<String, Object> dsMap) {
              try {
                  Object type = dsMap.get("type");
                  if (type == null)
                      type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
      
                  Class<? extends DataSource> dataSourceType;
                  dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
                  String driverClassName = dsMap.get("driver-class-name").toString();
                  String url = dsMap.get("url").toString();
                  String username = dsMap.get("username").toString();
                  String password = dsMap.get("password").toString();
                  DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
                          .username(username).password(password).type(dataSourceType);
                  return factory.build();
              } catch (Throwable e) {
                  log.error("buildDataSource failed!",e);
              }
              return null;
          }
      
          /**
           * 加载多数据源配置
           */
          @Override
          public void setEnvironment(Environment environment) {
              this.env = environment;
              binder = Binder.get(env); //绑定配置器
              initDefaultDataSource();
              initCustomDataSources();
          }
      
          /**
           * 初始化主数据源
           *
           * @author SHANHY
           * @create 2016年1月24日
           */
          private void initDefaultDataSource() {
              //读取数据源参数配置
              Map props = binder.bind("spring.datasource", Map.class).get();
              Map<String, Object> dsMap = new HashMap<>();
              dsMap.put("type", props.get("type"));
              dsMap.put("driver-class-name", props.get("driver-class-name"));
              dsMap.put("url", props.get("url"));
              dsMap.put("username", props.get("username"));
              dsMap.put("password", props.get("password"));
              defaultDataSource = buildDataSource(dsMap);
              dataBinder(defaultDataSource, props);
          }
      
          /**
           * 初始化更多数据源
           *
           * @author SHANHY
           * @create 2016年1月24日
           */
          private void initCustomDataSources() {
              // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
              List<Map> configs = binder.bind("custom.datasource", Bindable.listOf(Map.class)).get();
              String dsPrefix;
              DataSource custom;
              for (Map config : configs) {
                  dsPrefix = requireNotSatisfy(p -> StringUtils.isNotEmpty(config.get("key").toString()), config.get("key").toString(), "default");
                  custom = buildDataSource(config);
                  customDataSources.put(dsPrefix, custom);
                  dataBinder(custom, config);
              }
          }
      
          /**
           * 绑定参数,以下三个方法都是参考DataSourceBuilder的bind方法实现的,
           * 目的是尽量保证我们自己添加的数据源构造过程与spring-boot保持一致
           *
           * @param dataSource
           * @param properties
           */
          private void dataBinder(DataSource dataSource, Map properties) {
              ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
              Binder binderEx = new Binder(source.withAliases(aliases));
              binderEx.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(dataSource));  //将参数绑定到对象
          }
      
      }
    
    • DynamicDataSourceContextHolder缓存当前线程上下文中数据源标识ID
    /*
     * @ProjectName: 编程学习
     * @Copyright:   2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
     * @address:     http://xiazhaoyang.tech
     * @date:        2019/3/23 16:57
     * @email:       xiazhaoyang@live.com
     * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
     */
    package com.example.common.config.database;
    
    import com.example.common.util.CapsuleStringUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import static com.example.common.util.CapsuleStringUtil.requireNotSatisfyThrowException;
    
    /**
     * <p>
     *
     * </p>
     *
     * @author xiazhaoyang
     * @version v1.0.0
     * @date 2019/3/23 16:57
     * @modificationHistory=========================逻辑或功能性重大变更记录
     * @modify By: {修改人} 2019/3/23
     * @modify reason: {方法名}:{原因}
     * ...
     */
    @Slf4j
    class DynamicDataSourceContextHolder {
    
        //保存当前线程的数据源对应的key
        private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    
        static List<String> dataSourceIds = new ArrayList<>();
    
        static void setDataSourceType(String dataSourceType) {
            contextHolder.set(dataSourceType);
        }
    
        static String getDataSourceType() {
            try {
                return requireNotSatisfyThrowException(p->StringUtils.isNotBlank(contextHolder.get()),contextHolder.get(),"can not found datasource by key: '%s',this session may use default datasource","");
            } catch (NullPointerException e) {
                contextHolder.set("dataSource");
                log.error(e.getMessage());
                //如果动态数据源获取为空,返回默认数据源
                return contextHolder.get();
            }
        }
    
        static void clearDataSourceType() {
            contextHolder.remove();
        }
    
        /**
         *
         * 判断指定DataSrouce当前是否存在
         * @return
         * @author xiazhaoyang
         * @date 2019/3/24 17:52
         * @modify by: {修改人} 2019/3/24 17:52
         * @modify by reason:
         * @since 1.0.0
         */
        static boolean containsDataSource(String dataSourceId){
            return dataSourceIds.contains(dataSourceId);
        }
    }
    
    
    • DataSourceDynamicAspectAOP切片解析注解
      /*
       * @ProjectName: 编程学习
       * @Copyright:   2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
       * @address:     http://xiazhaoyang.tech
       * @date:        2019/3/24 11:03
       * @email:       xiazhaoyang@live.com
       * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
       */
      package com.example.common.config.database;
      
      import org.aspectj.lang.annotation.Aspect;
      import org.springframework.core.annotation.Order;
      import lombok.extern.slf4j.Slf4j;
      import org.aspectj.lang.annotation.*;
      import org.springframework.stereotype.Component;
      import org.aspectj.lang.JoinPoint;
      
      /**
       * <p>
       * 声明数据源切面
       * </p>
       *
       * @author xiazhaoyang
       * @version v1.0.0
       * @date 2019/3/24 11:03
       * @modificationHistory=========================逻辑或功能性重大变更记录
       * @modify By: {修改人} 2019/3/24
       * @modify reason: {方法名}:{原因}
       * ...
       */
      @Component
      @Aspect
      @Order(-10) //使该切面在事务之前执行
      @Slf4j
      public class DataSourceDynamicAspect {
      
          /**
           * AOP切面拦截注解 TargetDataSource 先从当前线程中取出数据库标识
           * @param point
           * @param ds
           */
          @Before("@annotation(ds)")
          public void changeDataSource(JoinPoint point, TargetDataSource ds) {
              String dsId = ds.name();
              if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
                  log.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature());
              } else {
                  log.debug("Use DataSource : {} > {}", ds.name(), point.getSignature());
                  DynamicDataSourceContextHolder.setDataSourceType(ds.name());
              }
          }
      
          /**
           * AOP切面拦截注解 TargetDataSource 从当前线程中删除数据库标识
           * @param point
           * @param ds
           */
          @After("@annotation(ds)")
          public void restoreDataSource(JoinPoint point, TargetDataSource ds) {
              log.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());
              DynamicDataSourceContextHolder.clearDataSourceType();
          }
      
      
      }
    
    • DynamicDataSource动态数据源对象定义,用于数据源实例注册到Spring实例工厂后的路由
    /*
     * @ProjectName: 编程学习
     * @Copyright:   2019 HangZhou xiazhaoyang Dev, Ltd. All Right Reserved.
     * @address:     http://xiazhaoyang.tech
     * @date:        2019/3/23 16:56
     * @email:       xiazhaoyang@live.com
     * @description: 本内容仅限于编程技术学习使用,转发请注明出处.
     */
    package com.example.common.config.database;
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    /**
     * <p>
     *
     * </p>
     *
     * @author xiazhaoyang
     * @version v1.0.0
     * @date 2019/3/23 16:56
     * @modificationHistory=========================逻辑或功能性重大变更记录
     * @modify By: {修改人} 2019/3/23
     * @modify reason: {方法名}:{原因}
     * ...
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
    
    
        /**
         * AbstractRoutingDataSource抽象类实现方法,即获取当前线程数据源的key
         *
         * @return data unique key
         */
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicDataSourceContextHolder.getDataSourceType();
        }
    }
    

    AbstractRoutingDataSource

    Abstract {@link javax.sql.DataSource} implementation that routes {@link #getConnection()}
    calls to one of various target DataSources based on a lookup key. The latter is usually
    (but not necessarily) determined through some thread-bound transaction context.
    基于查找标识键来调用各种目标数据源之一的路由 {@link #getConnection ()} 的抽象实现{@link javax.sql.DataSource}。后者通常是(但不一定) 通过某些线程绑定事务上下文确定。
    

    效果

    • 账号信息查询

    在这里插入图片描述

    • 人员信息查询

      在这里插入图片描述

    REFRENCES

    更多

    扫码关注“架构探险之道”,获取更多源码和文章资源

    在这里插入图片描述

    知识星球(扫码加入获取源码和文章资源链接)

    在这里插入图片描述

    展开全文
  • Spring Boot中通过依赖spring-boot-starter-data-mongodb,来实现spring-data-mongodb的自动配置,我们最终使用MongoDB操作数据库是通过MongoTemplate类完成,他提供增删改查等方法 从上图可以看出MongoTemplate...

    实现原理:

    Spring Boot中通过依赖spring-boot-starter-data-mongodb,来实现spring-data-mongodb的自动配置,我们最终使用MongoDB操作数据库是通过 MongoTemplate类完成,他提供增删改查等方法

    从上图可以看出MongoTemplate的连接配置可由MongoDbFactory 生成,从而提供了自定义连接的可能,

    实际上,spring-data-mongodb的自动配置也是如此。

    所以,可以创建多个一个MongoDbFactory 来构造不同的MongoTemplate,然后在操作数据库时根据使用场景注入不同的MongoTemplate来使用。

    用数据库连接uri 创建 MongoClientURI ,再生成 SimpleMongoDbFactory,最后创建 MongoTemplate即可。

    编码要点

    首先定义多个数据库的配置,代码对应下边第〇点

    然后读取数据库的配置,代码对应下边第一点

    读取对应的配置信息并且装配指定的MongoTemplate,第三点

    所有文件:
    application.yml 
    MultipleMongoProperties 
    PrimaryMongoConfig
    SecondaryMongoConfig
    MultipleMongoConfig
    PersonTest
    MongoTestC 

    完整文件以及代码

    〇、application.yml 加上如下数据库配置

    mongodb:
      primary:
        host: 192.168.1.2
        port: 27017
        database: primary
        #username: admin
        #password: admin
    
      secondary:
        host: 192.168.1.3
        port: 27017
        database: secondary
        #username: admin
        #password: admin

    平时单个数据库配置格式为:spring:data:mongodb:... 是因为spring boot 默认的资源加载格式。这里mongodb开头是自定义。没有这个疑问的伙伴可以忽略这段话。。。

    一、配置数据库的数据源:

    通过@ConfigurationProperties(prefix = "mongodb")读取配置文件的mongodb节点配置

    import org.springframework.boot.autoconfigure.mongo.MongoProperties;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    /**
     * @ClassName: MultipleMongoProperties
     * @Description: 从配置文件加载数据源
     * @Author Tan
     * @Date 2019/7/3
     */
    @Component
    @ConfigurationProperties(prefix = "mongodb")
    public class MultipleMongoProperties {
    
        private MongoProperties primary = new MongoProperties();
        
        private MongoProperties secondary = new MongoProperties();
    
        public MongoProperties getPrimary() {
            return primary;
        }
    
        public void setPrimary(MongoProperties primary) {
            this.primary = primary;
        }
    
        public MongoProperties getSecondary() {
            return secondary;
        }
    
        public void setSecondary(MongoProperties secondary) {
            this.secondary = secondary;
        }
    }

    二、两个数据库接口封装配置

    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
    
    /**
     * @ClassName: PrimaryMongoConfig
     * @Description: TODO
     * @Author Tan
     * @Date 2019/7/3
     */
    @Configuration
    @EnableMongoRepositories(mongoTemplateRef = PrimaryMongoConfig.MONGO_TEMPLATE)
    public class PrimaryMongoConfig {
        public static final String MONGO_TEMPLATE = "primaryMongoTemplate";
    
    }
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
    
    /**
     * @ClassName: PrimaryMongoConfig
     * @Description: TODO
     * @Author TanXiongZhan
     * @Date 2019/7/3
     */
    @Configuration
    @EnableMongoRepositories(mongoTemplateRef = SecondaryMongoConfig.MONGO_TEMPLATE)
    public class SecondaryMongoConfig {
        public static final String MONGO_TEMPLATE = "secondaryMongoTemplate";
    
    }

    三、读取对应的配置信息并且装配对应的MongoTemplate(关键的一步)

    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.mongo.MongoProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.data.mongodb.MongoDbFactory;
    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
    
    /**
     * @ClassName: MultipleMongoConfig
     * @Description: 读取对应的配置信息并且构造对应的MongoTemplate
     * @Author Tan
     * @Date 2019/7/3
     */
    @Configuration
    public class MultipleMongoConfig {
    
        @Autowired
        private MultipleMongoProperties mongoProperties;
    
        @Primary
        @Bean(name = PrimaryMongoConfig.MONGO_TEMPLATE)
        public MongoTemplate primaryMongoTemplate() throws Exception {
            return new MongoTemplate(primaryFactory(this.mongoProperties.getPrimary()));
        }
    
        @Bean
        @Qualifier(SecondaryMongoConfig.MONGO_TEMPLATE)
        public MongoTemplate secondaryMongoTemplate() throws Exception {
            return new MongoTemplate(secondaryFactory(this.mongoProperties.getSecondary()));
        }
    
        @Bean
        @Primary
        public MongoDbFactory primaryFactory(MongoProperties mongo) throws Exception {
            if (StringUtils.isEmpty(mongo.getUsername()) || mongo.getPassword() == null ) {
                return new SimpleMongoDbFactory(new MongoClient(mongo.getHost(), mongo.getPort()),
                        mongo.getDatabase());
            } else {
                String password = String.valueOf(mongo.getPassword());
                String url = "mongodb://" + mongo.getUsername()
                        + ":" + password
                        + "@" + mongo.getHost() + ":" + mongo.getPort() + "/" + mongo.getDatabase()
                        + "?authSource=admin&authMechanism=SCRAM-SHA-1";
                return new SimpleMongoDbFactory(new MongoClientURI(url));
            }
        }
    
        @Bean
        public MongoDbFactory secondaryFactory(MongoProperties mongo) throws Exception {
            if (StringUtils.isEmpty(mongo.getUsername()) || mongo.getPassword() == null ) {
                        return new SimpleMongoDbFactory(new MongoClient(mongo.getHost(), mongo.getPort()),
                        mongo.getDatabase());
            } else {
                String password = String.valueOf(mongo.getPassword());
                String url = "mongodb://" + mongo.getUsername()
                        + ":" + password
                        + "@" + mongo.getHost() + ":" + mongo.getPort() + "/" + mongo.getDatabase()
                        + "?authSource=admin&authMechanism=SCRAM-SHA-1";
                return new SimpleMongoDbFactory(new MongoClientURI(url));
            }
        }
    
    }
    

    说明:@Primary 表明默认加载,假如使用的时候不表明具体的MongoTemplate,则使用@Primary所示的。如

    @Autowired 
    private MongoTemplate mongoTemplate;

     则得到的 mongoTemplate 对象就是 primaryMongoTemplate

    四、使用

    1、定义实体对象

    public class PersonTest {
    
        private Integer id;
        private Integer age;
        private String name;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    }

    2、测试与应用:

    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    
    @RestController
    @RequestMapping("mongoTest")
    public class MongoTestC {
    
        @Resource(name = PrimaryMongoConfig.MONGO_TEMPLATE)
        private MongoTemplate primaryMongoTemplate;     //第一个库的MongoTemplate
    
        @Resource(name = SecondaryMongoConfig.MONGO_TEMPLATE)
        private MongoTemplate secondaryMongoTemplate;   //第二个库的MongoTemplate
    
        @Autowired 
        private MongoTemplate mongoTemplate;            //第一个库的MongoTemplate
    
        @GetMapping(value = "/test1")
        public void saveTest() throws Exception {
            PersonTest mgtest = new PersonTest();
            mgtest.setId(8);
            mgtest.setAge(88);
            mgtest.setName("8888");
            primaryMongoTemplate.save(mgtest);
            secondaryMongoTemplate.save(mgtest);
        }
    }

     

    展开全文
  • Spring Boot Jpa 多数据源

    2018-11-09 09:42:17
    Spring Boot 从 1.5 升级到 2.0 再到 2.1.0 多数据源的配置一直再改变,本文主要对于 spring boot jpa 的多数据源配置进行说明,并给出各个版本的样例代码及注意要点

    前言

    随着业务量发展,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源,如:user一个库,business一个库。那么接下来我们就要考虑怎么去在spring boot中实现多个数据源的配置。

    源码下载

    实现

    • 建表

      首先是建表语句,我们要建立两个数据库,并各库内新建一张表

      user表​

      mysql> use `user`;
      mysql> select * from `user`;
      +----+-------+----------+
      | id | name  | password |
      +----+-------+----------+
      |  1 | 用户A | ******   |
      +----+-------+----------+
      1 row in set
      

      business表

      mysql> use `business`;
      mysql> select * from `business`;
      +----+-------+-------------+
      | id | name  | description |
      +----+-------+-------------+
      |  1 | 业务A | 业务A描述   |
      +----+-------+-------------+
      1 row in set
      

      接下来我们通过代码实现对两个库内的多张表进行查询。

    • 配置

      首先,创建一个Spring配置类,定义两个DataSource用来读取application.yml中的不同配置。本文中,我们user做为主数据源,主数据源配置为spring.datasource.user开头的配置,business数据源配置为spring.datasource.business开头的配置。

      @Configuration
      public class DataSourceConfig {
          @Primary
          @Bean(name = "userDataSource")
          @Qualifier("userDataSource")
          @ConfigurationProperties(prefix = "spring.datasource.user")
          public DataSource userDataSource() {
              return DataSourceBuilder.create().build();
          }
      
          @Bean(name = "businessDataSource")
          @Qualifier("businessDataSource")
          @ConfigurationProperties(prefix = "spring.datasource.business")
          public DataSource businessDataSource() {
              return DataSourceBuilder.create().build();
          }
      }
      

      对应的配置文件application.yml如下:

      spring:
        datasource:
          user:
            driver-class-name: com.mysql.jdbc.Driver
            jdbc-url: jdbc:mysql://localhost:3306/user
            username: root
            password: 123456
          business:
            driver-class-name: com.mysql.jdbc.Driver
            jdbc-url: jdbc:mysql://localhost:3306/business
            username: root
            password: 123456
      

      接下来我们对各数据源进行jpa的配置

      主数据源User

      @Configuration
      @EnableTransactionManagement
      @EnableJpaRepositories(
              entityManagerFactoryRef = "entityManagerFactoryUser",
              transactionManagerRef = "transactionManagerUser",
              //设置Repository所在位置
              basePackages = {"com.ppc.spring.example.jpamultidatasource.repository.user"})
      public class UserConfig {
      
          @Autowired
          @Qualifier("userDataSource")
          private DataSource userDataSource;
          @Autowired
          private JpaProperties jpaProperties;
          @Autowired
          private HibernateProperties hibernateProperties;
      
          @Primary
          @Bean(name = "entityManagerUser")
          public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
              return entityManagerFactoryUser(builder).getObject().createEntityManager();
          }
      
          @Primary
          @Bean(name = "entityManagerFactoryUser")
          public LocalContainerEntityManagerFactoryBean entityManagerFactoryUser(EntityManagerFactoryBuilder builder) {
              return builder
                      .dataSource(userDataSource)
                      //设置entity所在位置
                      .packages("com.ppc.spring.example.jpamultidatasource.entity.user")
                      .persistenceUnit("userPersistenceUnit")
                      .properties(getVendorProperties())
                      .build();
          }
      
          private Map<String, Object> getVendorProperties() {
              return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
          }
      
          @Primary
          @Bean(name = "transactionManagerUser")
          public PlatformTransactionManager transactionManagerUser(EntityManagerFactoryBuilder builder) {
              return new JpaTransactionManager(entityManagerFactoryUser(builder).getObject());
          }
      
      }
      

      其他数据源business

      @Configuration
      @EnableTransactionManagement
      @EnableJpaRepositories(
              entityManagerFactoryRef = "entityManagerFactoryBusiness",
              transactionManagerRef = "transactionManagerBusiness",
              //设置repository所在位置
              basePackages = {"com.ppc.spring.example.jpamultidatasource.repository.business"})
      public class BusinessConfig {
      
          @Autowired
          @Qualifier("businessDataSource")
          private DataSource businessDataSource;
          @Autowired
          private JpaProperties jpaProperties;
          @Autowired
          private HibernateProperties hibernateProperties;
      
          @Bean(name = "entityManagerBusiness")
          public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
              return entityManagerFactoryBusiness(builder).getObject().createEntityManager();
          }
      
          @Bean(name = "entityManagerFactoryBusiness")
          public LocalContainerEntityManagerFactoryBean entityManagerFactoryBusiness(EntityManagerFactoryBuilder builder) {
              return builder
                      .dataSource(businessDataSource)
                      .properties(getVendorProperties())
                      //设置实体类所在位置
                      .packages("com.ppc.spring.example.jpamultidatasource.entity.business")
                      .persistenceUnit("businessPersistenceUnit")
                      .build();
          }
      
          private Map<String, Object> getVendorProperties() {
              return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
          }
      
          @Bean(name = "transactionManagerBusiness")
          PlatformTransactionManager transactionManagerBusiness(EntityManagerFactoryBuilder builder) {
              return new JpaTransactionManager(entityManagerFactoryBusiness(builder).getObject());
          }
      }
      

      配置中需要注意以下几点:

      • repository、entity的所在位置,要和实际保存的位置一致。

      • 主数据源的一些配置需要添加@Primary作为spring默认的首选项,其他数据源无需添加该注解。

      • 通过查看相关源码我们看到Spring Boot中JpaProperties的代码一直在调整,这里我们将properties相关代码单独提取出作为一个单独的方法getVendorProperties展示版本间的区别。其中:

        • Spring Boot 1.5.x

          private Map<String, String> getVendorProperties() {
            return jpaProperties.getHibernateProperties(userDataSource);
          }
          
        • Spring Boot 2.0.x

          private Map<String, Object> getVendorProperties() {
            return jpaProperties.getHibernateProperties(new HibernateSettings());
          }
          
        • Spring Boot 2.1.0参见上文代码,引进了HibernateProperties。同时,在Spring Boot 2.1.0中默认的mysql-connector-java版本为8.0.13,连接低版本mysql配置上比较繁琐,建议在配置文件中手动指定相应版本,如本文中指定5.1.46这个版本。

          runtimeOnly('mysql:mysql-connector-java:5.1.46')
          
    • 查询

      完成了所有的配置,接下来我们就可以开始写个简单代码验证我们配置了

      @RestController
      @SpringBootApplication
      public class JpaMultiDatasourceApplication {
          @Autowired
          UserRepository userRepository;
          @Autowired
          BusinessRepository businessRepository;
      
          public static void main(String[] args) {
              SpringApplication.run(JpaMultiDatasourceApplication.class, args);
          }
      
          @GetMapping("/user/{id}")
          public User getUser(@PathVariable Long id) {
              return userRepository.findById(id).orElse(null);
          }
      
          @GetMapping("/business/{id}")
          public Business getBusiness(@PathVariable Long id) {
              return businessRepository.findById(id).orElse(null);
          }
      }
      

      我们对外暴露了两个接口,分别访问user表business表确认可以正常获取数据。查询结果如下:

      请求:http://localhost:8080/user/1
      结果:{"id":1,"name":"用户A","password":"******"}
      
      请求:http://localhost:8080/business/1
      结果:{"id":1,"name":"业务A","description":"业务A描述"}
      

      就此,我们双数据源的配置和验证工作就完成了。

    展开全文
  • Spring boot配置多个Redis数据源操作实例 在SpringBoot是项目中整合了两个Redis的操作实例,可以增加多个; 一般在一个微服务生态群中是不会出现多个Redis中间件的,所以这种场景很少见,但也不可避免,但是不建议...

    Spring boot配置多个Redis数据源操作实例

    在SpringBoot是项目中整合了两个Redis的操作实例,可以增加多个;
    一般在一个微服务生态群中是不会出现多个Redis中间件的,所以这种场景很少见,但也不可避免,但是不建议使用,个人建议,勿喷。

    • 基于Maven3.0搭建,spring1.5.9.RELEASE和JDK1.8

    1、新建SpringBoot项目,添加依赖

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    

    2、application.yml配置文件

    spring:
      redis:
        database: 6   # Redis数据库索引(默认为0)
        host: redis.lilian.com  # Redis服务器地址
        port: 7481  # Redis服务器连接端口
        password:    # Redis服务器连接密码(默认为空)
        timeout: 0  # 连接超时时间(毫秒)
        pool:
          max-active: -1 # 连接池最大连接数(使用负值表示没有限制)
          max-wait: -1  # 连接池最大阻塞等待时间(使用负值表示没有限制)
          max-idle: 8  # 连接池中的最大空闲连接
          min-idle: 0  # 连接池中的最小空闲连接
      redis2:
        database: 6   # Redis数据库索引(默认为0)
        host: redis.lilian.com  # Redis服务器地址
        port: 7480  # Redis服务器连接端口
        password:    # Redis服务器连接密码(默认为空)
        timeout: 0  # 连接超时时间(毫秒)
    

    3、新建RedisConfig类

    package com.lilian.config;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.cache.interceptor.KeyGenerator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import redis.clients.jedis.JedisPoolConfig;
    
    import java.lang.reflect.Method;
    
    /**
     * spring-boot-data-packing 设置Redis多实例的基类
     *
     * @Author 孙龙
     * @Date 2018/8/13
     */
    @EnableCaching
    @Configuration
    public class RedisConfig {
        @Value("${spring.redis.pool.max-active}")
        private int redisPoolMaxActive;
    
        @Value("${spring.redis.pool.max-wait}")
        private int redisPoolMaxWait;
    
        @Value("${spring.redis.pool.max-idle}")
        private int redisPoolMaxIdle;
    
        @Value("${spring.redis.pool.min-idle}")
        private int redisPoolMinIdle;
    
        /**
         * 配置Key的生成方式
         *
         * @return
         */
        @Bean
        public KeyGenerator keyGenerator() {
            return new KeyGenerator() {
                @Override
                public Object generate(Object o, Method method, Object... objects) {
                    StringBuilder stringBuilder = new StringBuilder();
                    stringBuilder.append(o.getClass().getName())
                            .append(method.getName());
                    for (Object object : objects) {
                        stringBuilder.append(object.toString());
                    }
                    return stringBuilder.toString();
                }
            };
        }
    
        /**
         * 创建redis连接工厂
         *
         * @param dbIndex
         * @param host
         * @param port
         * @param password
         * @param timeout
         * @return
         */
        public JedisConnectionFactory createJedisConnectionFactory(int dbIndex, String host, int port, String password, int timeout) {
            JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
            jedisConnectionFactory.setDatabase(dbIndex);
            jedisConnectionFactory.setHostName(host);
            jedisConnectionFactory.setPort(port);
            jedisConnectionFactory.setPassword(password);
            jedisConnectionFactory.setTimeout(timeout);
            jedisConnectionFactory.setPoolConfig(setPoolConfig(redisPoolMaxIdle, redisPoolMinIdle, redisPoolMaxActive, redisPoolMaxWait, true));
            return jedisConnectionFactory;
    
        }
    
        /**
         * 配置CacheManager
         *
         * @param redisTemplate
         * @return
         */
        @Bean
        public CacheManager cacheManager(RedisTemplate redisTemplate) {
            RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
            return redisCacheManager;
        }
    
        /**
         * 设置连接池属性
         *
         * @param maxIdle
         * @param minIdle
         * @param maxActive
         * @param maxWait
         * @param testOnBorrow
         * @return
         */
        public JedisPoolConfig setPoolConfig(int maxIdle, int minIdle, int maxActive, int maxWait, boolean testOnBorrow) {
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            poolConfig.setMaxIdle(maxIdle);
            poolConfig.setMinIdle(minIdle);
            poolConfig.setMaxTotal(maxActive);
            poolConfig.setMaxWaitMillis(maxWait);
            poolConfig.setTestOnBorrow(testOnBorrow);
            return poolConfig;
        }
    
        /**
         * 设置RedisTemplate的序列化方式
         *
         * @param redisTemplate
         */
        public void setSerializer(RedisTemplate redisTemplate) {
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            //设置键(key)的序列化方式
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            //设置值(value)的序列化方式
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.afterPropertiesSet();
        }
    }
    

    4、使用Java类注入多个数据源

    • 数据源一
    package com.lilian.config;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    
    /**
     * llld-parent 配置默认Redis操作实例 到Spring中
     *
     * @Author 孙龙
     * @Date 2018/8/2
     */
    @Configuration
    @EnableCaching
    public class DefaultRedisConfig extends RedisConfig {
    
    
        @Value("${spring.redis.database}")
        private int dbIndex;
    
        @Value("${spring.redis.host}")
        private String host;
    
        @Value("${spring.redis.port}")
        private int port;
    
        @Value("${spring.redis.password}")
        private String password;
    
        @Value("${spring.redis.timeout}")
        private int timeout;
    
        /**
         * 配置redis连接工厂
         *
         * @return
         */
        @Bean
        public RedisConnectionFactory defaultRedisConnectionFactory() {
            return createJedisConnectionFactory(dbIndex, host, port, password, timeout);
        }
    
        /**
         * 配置redisTemplate 注入方式使用@Resource(name="") 方式注入
         *
         * @return
         */
        @Bean(name = "defaultRedisTemplate")
        public RedisTemplate defaultRedisTemplate() {
            RedisTemplate template = new RedisTemplate();
            template.setConnectionFactory(defaultRedisConnectionFactory());
            setSerializer(template);
            template.afterPropertiesSet();
            return template;
        }
    }
    
    • 数据源二
    package com.lilian.config;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    
    /**
     * llld-parent 配置缓存Redis操作实例 到Spring中
     *
     * @Author 孙龙
     * @Date 2018/8/2
     */
    @Configuration
    @EnableCaching
    public class CacheRedisConfig extends RedisConfig {
    
    
        @Value("${spring.redis2.database}")
        private int dbIndex;
    
        @Value("${spring.redis2.host}")
        private String host;
    
        @Value("${spring.redis2.port}")
        private int port;
    
        @Value("${spring.redis2.password}")
        private String password;
    
        @Value("${spring.redis2.timeout}")
        private int timeout;
    
        /**
         * 配置redis连接工厂
         *
         * @return
         */
        @Primary
        @Bean
        public RedisConnectionFactory cacheRedisConnectionFactory() {
            return createJedisConnectionFactory(dbIndex, host, port, password, timeout);
        }
    
        /**
         * 配置redisTemplate 注入方式使用@Resource(name="") 方式注入
         *
         * @return
         */
        @Bean(name = "cacheRedisTemplate")
        public RedisTemplate cacheRedisTemplate() {
            RedisTemplate template = new RedisTemplate();
            template.setConnectionFactory(cacheRedisConnectionFactory());
            setSerializer(template);
            template.afterPropertiesSet();
            return template;
        }
    
    }
    
    • 数据源三同理。。。

    5、随便定义一个实体类

    package com.lilian.entity;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    
    /**
     * jpa-demo
     *
     * @Author 孙龙
     * @Date 2018/7/3
     */
    @Data
    @AllArgsConstructor
    public class Person {
    
        /**
         * 姓名
         */
        private String name;
        /**
         * 年龄
         */
        private Integer age;
        /**
         * 地址
         */
        private String address;
        /**
         * 邮箱
         */
        private String email;
        /**
         * 手机号码
         */
        private String phoneNum;
    
    }
    

    6、测试方法

    package com.lilian;
    
    import com.lilian.entity.Person;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import javax.annotation.Resource;
    
    /**
     * spring-boot-data-packing
     *
     * @Author 孙龙
     * @Date 2018/8/13
     */
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class MultiRedisTest {
    
        @Resource(name = "defaultRedisTemplate")
        private RedisTemplate<String, Object> redisTemplate;
    
        @Resource(name = "cacheRedisTemplate")
        private RedisTemplate<String, Object> redisTemplate1;
    
        @Test
        public void stringRedisTest() {
    
            redisTemplate.opsForValue().set("slzzzz", "111111");
            redisTemplate1.opsForValue().set("slzzzz", "222222");
    
        }
    
        @Test
        public void objectRedisTest() {
            redisTemplate.opsForValue().set("person", new Person("李飞", 20, "临汾", "lf@lilian.com", "1324567891"));
            redisTemplate1.opsForValue().set("person", new Person("李大壮", 35, "西安", "ldz@lilian.com", "1324567891"));
        }
    
    }

    声明:该博文是自己结合其他博主经验自己实践的一些总结,希望能帮到你,如果帮到你请帮我点一个星星;
    Github代码示例
    参考博客:http://www.cnblogs.com/lchb/articles/7222870.html

    链接:https://www.jianshu.com/p/c79b65b253fa

     

    展开全文
  • Spring Boot 集成Mybatis实现多数据源

    万次阅读 热门讨论 2017-06-30 19:12:38
    项目提交测试,趁着中当间的这空档期,把springboot的多数据源配置学习一下,总体来说多数据源配置有两种方式,一种是静态的,一种是动态的。 静态的方式 我们以两套配置方式为例,在项目中有两套配置文件,两套...
  • Spring Boot项目在整合Mybatis过程中全部使用了注解配置,由于业务系统涉及多个数据源,查阅后整理输出供需要的伙伴参考。 难点 由于全部使用@Mapper注解在接口中定义了DAO层逻辑,且在使用过程中直接使用@...
  • Spring和SpringBoot中,对此都有相应的解决方案,不过一般来说,如果有多数据源的需求,我还是建议首选分布式数据库中间件MyCat去解决相关问题,如果说数据根据条件的不同,可能保存在四十多个不同的数据库中,那...
  • Spring boot如果配置单数据源,使用起来非常简单,几注解就可以完成数据库的连接,但是如果我们需要配置多数据源,那一些必要的代码就需要我们自己去编写,完成与某个数据库的连接。 一、Spring boot的默认单数据...
  • 使用Liquibase的Spring Boot多个数据源 多个数据源 您需要向yaml配置文件中添加几个数据源定义。 datasource: primary: url: jdbc:mysql://localhost/primary username: root driver-class-name: ...
  • Spring Boot 1.x下,我们的多数据源配置如下: 读数据库 @Configuration @EnableTransactionManagement @EnableJpaRepositories(entityManagerFactoryRef = &amp;quot;entityManagerFactoryPrimary&amp;...
  • 如何通过Spring Boot配置动态数据源访问多个数据库

    万次阅读 多人点赞 2018-03-18 14:59:52
    之前写过一篇博客《Spring+Mybatis+Mysql搭建分布式数据库访问框架》描述如何通过Spring+Mybatis配置动态数据源访问多个数据库。但是之前的方案有一些限制(原博客中也描述了):只适用于数据库数量不多且固定的情况。...
  • Spring Boot多数据源优雅实践

    千次阅读 2018-05-15 12:59:55
    前两天有小伙伴谈起Boot实现多数据源的解决方案,我之前有见过一个方案是实例化多个数据源bean,每个数据源对应一套独立的mybatis映射(dao接口和xml),首先这个方案是可以解决问题的,但是随之带来的问题是你得...
  • spring-boot和JPA多数据源整合

    千次阅读 2019-06-02 00:01:43
    今天弄一下spring-boot和jpa的多数据源整合 Jpa(Java Persistence API)Java持久化API,它是一套ORM规范 只是一套规范 Spring Boot中使用的Jpa实际上是Spring Data Jpa,Spring Data是Spring家族的一子项目,用于...
  • Spring Boot Jpa多数据源配置

    千次阅读 2018-11-12 21:55:12
    那么接下来我们就要考虑怎么去在spring boot中实现多个数据源的配置。 源码下载 实现 建表 首先是建表语句,我们要建立两个数据库,并各内新建一张表 user表 mysql&gt; use `user`; mysql&gt; ...
  • 而在spring boot2.x版本中,hikari终于荣升为默认数据源,已被包含在spring boot的jdbc依赖组件中。参数配置在properties中配置以下参数,前三是通用配置,注意第四参数maximumPoolSize有一hikari前缀,代表.....
  • 14.玩转Spring Boot 多数据源

    千次阅读 2016-12-28 11:33:14
    在项目中有的时候需要用到多个数据源,有个问题就是单数据源的事务是没有问题的,多数据源是会存在事务问题的。这里不做事务讲解,事务可以用JTA分布式事务,也可以用MQ。具体不做叙述,接下来说如何实现多数据源...
  • 转载请注明出处 :Spring Boot使用spring-data-jpa配置Mysql多数据源 我们在之前的文章中已经学习了Spring Boot中...但是往往随着业务量发展,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据...
  • spring boot配置多个数据源(oracle)

    千次阅读 2020-01-02 18:30:06
    在自己的项目下新建一包,新建文件依次复制粘贴下列代码 1 package ***********.datasource; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation...
  • spring boot druid mybatis 多数据源 配置

    千次阅读 2017-09-02 21:28:05
    spring boot 在配置时做了很简化配置的设置,但是简化的配置往往已牺牲一定的定制化,比如在数据源的配置时,spring boot 只提供4种数据库连接池的配置,其中并不支持常用的druid 阅读spring boot DataSource...
  • Spring Boot 动态数据源多数据源自动切换)

    万次阅读 多人点赞 2016-01-24 20:04:43
    本文实现案例场景: 某系统除了需要从自己的主要数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库,要求可以在任何方法上可以灵活指定具体要操作的数据库。...1. 配置文件中配置多个数据源 2. 在需要
  • spring boot的Jdbc多数据源配置实战

    千次阅读 2018-10-28 21:19:19
    一 新建依赖 &lt;dependencies&...org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-starter&lt;/artifactId&gt; &lt;/dependenc...
  • 由于Tomcat数据源连接池的性能和并发, 在tomcat可用时, 我们总是优先使用它。 如果HikariCP可用, 我们将使用它。 如果Commons DBCP可用, 我们将使用它, 但在生产环境不推荐使用它。 最后, 如果Commons DBCP...
  • Spring Boot下配置MyBatis多数据源

    千次阅读 2016-05-23 15:17:55
    Spring Boot最大的特点是简化开发,因此使用Java Config实现去xml配置,本文将使用这种方式完成对阿里云ADS和RDS两产品的多数据源配置。同时,会用到阿里巴巴的开源数据源Druid。 依赖 因为ADS和RDS都兼容mysql...
  • Spring Boot多数据源及其事务管理配置

    千次阅读 2018-06-21 13:57:00
    Spring Boot多数据源及其事务管理配置 Spring Boot多数据源及其事务管理配置 准备工作 配置文件 JavaConfig 配置数据源 配置JdbcTemplate 使用 事务配置 开启事务管理功能 配置事务管理器 使用 准备...
  • 而在spring boot2.x版本中,hikari终于荣升为默认数据源,已被包含在spring boot的jdbc依赖组件中。参数配置在properties中配置以下参数,前三是通用配置,注意第四参数maximumPoolSize有一hik...
  • Spring Boot 配置动态数据源 Spring Framework 为 SQL 数据库提供了广泛的支持。从直接使用 JdbcTemplate 进行 JDBC 访问到完 全的对象关系映射(object relational mapping)技术,比如 Hibernate。Spring Data ...
  • spring boot和Mybatis配置多个mysql数据源

    千次阅读 2018-09-28 16:40:04
    但有些业务场景下需要配置多个数据源,下面我们就介绍spring boot下配置两个mysql数据源: 在pom文件下添加依赖 &lt;!-- Druid 数据连接池依赖 --&gt; &lt;dependency&gt; &lt;groupId&...
  • 写这篇文章的初衷是应为最近一直在学习spring boot相关的一些东西,而在spring boot整合mybatis中相关的博客有很,可多数都是描述不清然人产生疑惑,或者干脆就是存在问题。尤其是在事务管理这里,大家说的都很...
  • Spring Boot多数据源配置与使用

    万次阅读 2017-01-06 09:42:45
    Spring Boot多数据源配置与使用  2016-03-28  Spring Boot 被围观 6048 次 之前在介绍使用JdbcTemplate和Spring-data-jpa时,都使用了单数据源。在单数据源的情况下,Spring Boot的配置非常简单,只需要在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,220
精华内容 14,888
关键字:

bootspring定义多个数据源

spring 订阅