druid多数据源配置+Datasurce动态切换方式

目录
  • druid多数据源配置+Datasurce动态切换
    • AbstractRoutingDataSource 数据源动态切换
    • 例子
  • 配置多数据源并实现Druid自动切换
    • 配置yml文件
    • 主数据源配置
    • 从数据源配置
    • 使用dao
    • 日志

druid多数据源配置+Datasurce动态切换

AbstractRoutingDataSource 数据源动态切换

spring 使用AbstractRoutingDataSource自定义动态数据源时的事务处理, 需要继承spring的AbstractRoutingDataSource定义自己的动态数据源,可以根据需要动态的切换不同数据库的数据源,使用起来非常方便。

public class ChooseDataSource extends AbstractRoutingDataSource {
 /**
  * 获取与数据源相关的key
  * 此key是Map<String,DataSource> resolvedDataSources 中与数据源绑定的key值
  * 在通过determineTargetDataSource获取目标数据源时使用
  */
 @Override
 protected Object determineCurrentLookupKey() {
  return RouteHolder.getRouteKey();
 }
}

通过容器RouteHolder存储当前线程使用的数据源的key

/**
 * 保存当前线程数据源的key
 */
public class RouteHolder {
 private static ThreadLocal<String> routeKey = new ThreadLocal<String>();

 /**
  * 获取当前线程的数据源路由的key
  * @return
  */
 public static String getRouteKey()
 {
  String key = routeKey.get();
  return key;
 }
 /**
  * 绑定当前线程数据源路由的key
  * 在使用完成之后,必须调用removeRouteKey()方法删除
  * @param key
  */
 public static void  setRouteKey(String key)
 {
  routeKey.set(key);
 }

 /**
  * 删除与当前线程绑定的数据源路由的key
  */
 public static void removeRouteKey()
 {
  routeKey.remove();
 }
}

使用spring 的aop编程在业务逻辑方法运行前将当前方法使用数据源的key从业务逻辑方法上自定义注解@DataSource中解析数据源key并添加到RouteHolder中

/**
 * 执行dao方法之前的切面
 * 获取datasource对象之前往RouteHolder中指定当前线程数据源路由的key
 *
 */
public class DataSourceAspect {
    /**
     * 在dao层方法之前获取datasource对象之前在切面中指定当前线程数据源路由的key
     */
    public void before(JoinPoint point)
    {
        Object target = point.getTarget();
        String method = point.getSignature().getName();
        Class<?>[] classz = target.getClass().getInterfaces();
        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
                .getMethod().getParameterTypes();
        try {
            if(classz != null && classz.length > 0) {
            Method m = classz[0].getMethod(method, parameterTypes);
            if (m != null && m.isAnnotationPresent(DataSource.class)) {
                DataSource data = m.getAnnotation(DataSource.class);
                RouteHolder.setRouteKey(data.value());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

解释:

DataSourceAspect 这个切面类,应该针对的被代理类应该是service的实现类(serviceImpl),因为dao层用的mybatis只有一个dao层的接口,所以放在service上做处理比较好。

业务逻辑方法

@Named("userService")
public class UserService
{
 @Inject
 private UserDao userDao;

 @DataSource("master")
 @Transactional(propagation=Propagation.REQUIRED)
 public void updatePasswd(int userid,String passwd)
 {
  User user = new User();
  user.setUserid(userid);
  user.setPassword(passwd);
  userDao.updatePassword(user);
 }
 @DataSource("slave")
 @Transactional(propagation=Propagation.REQUIRED)
 public User getUser(int userid)
 {
  User user = userDao.getUserById(userid);
  System.out.println("username------:"+user.getUsername());
  return user;
 }
}

注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
 * RUNTIME
 * 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。
 * @author jiangxm
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
    String value();
}

spring的配置文件

<bean id="dataSource" class="com.westone.datasource.DbRouteDataSource">
  <property name="targetDataSources">
    <map>
       <!-- write -->
       <entry key="master" value-ref="master"></entry>
       <!-- read -->
       <entry key="slave" value-ref="slave"></entry>
    </map>
  </property>
 </bean> 

 <bean id="master" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverclass}" />
        <property name="url" value="${jdbc.masterurl}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="maxActive" value="${jdbc.maxActive}"></property>
        <property name="maxIdle" value="${jdbc.maxIdle}"></property>
        <property name="maxWait" value="${jdbc.maxWait}"></property>
    </bean>  

    <bean id="slave" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverclass}" />
        <property name="url" value="${jdbc.slaveurl}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="maxActive" value="${jdbc.maxActive}"></property>
        <property name="maxIdle" value="${jdbc.maxIdle}"></property>
        <property name="maxWait" value="${jdbc.maxWait}"></property>
    </bean>    

    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" >
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:config/mybatis/mybatis.cfg.xml"></property>
        <property name="dataSource" ref="dataSource" />
    </bean> 

    <!--  配置mapper的映射扫描器 根据包中定义的接口自动生成dao的实现类-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <property name="basePackage" value="com.westone.dao"></property>
    </bean>   

   <!-- 为业务逻辑层的方法解析@DataSource注解  为当前线程的routeholder注入数据源key -->
    <bean id="aspectBean" class="com.westone.datasource.aspect.DataSourceAspect"></bean>

    <aop:config>
     <aop:aspect id="dataSourceAspect" ref="aspectBean">
         <aop:pointcut id="dataSourcePoint" expression="execution(public * com.westone.service.*.*(..))" />
         <aop:before method="beforeDaoMethod" pointcut-ref="dataSourcePoint"/>
     </aop:aspect>
    </aop:config>   

    <!-- 事务管理器配置 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
 <!-- 开启事务注解驱动    在业务逻辑层上使用@Transactional 注解 为业务逻辑层管理事务-->
    <tx:annotation-driven  transaction-manager="transactionManager"/>

事务管理配置一定要配置在往RouteHolder中注入数据源key之前 否则会报

Could not open JDBC Connection for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [null] 找不到数据源错误。

由此就可以根据方法上的@DataSource(“master”) 注解配置不同的数据源key 使用动态数据源。

解释:

java.lang.reflect.Method.getAnnotation(Class annotationClass)

参数:

annotationClass - Class对象对相应的注解类型,比如Datasource.class 。

返回值:

如果存在于此元素,则返回该元素注解指定的注解对象,否则返回为null

例子

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
public class MethodDemo {
   public static void main(String[] args) {
      Method[] methods = SampleClass.class.getMethods();
      Annotation annotation = methods[0].getAnnotation(CustomAnnotation.class);
      if(annotation instanceof CustomAnnotation){
         CustomAnnotation customAnnotation = (CustomAnnotation) annotation;
         System.out.println("name: " + customAnnotation.name());
         System.out.println("value: " + customAnnotation.value());
      }
   }
}
@CustomAnnotation(name="SampleClass",  value = "Sample Class Annotation")
class SampleClass {
   private String sampleField;
   @CustomAnnotation(name="getSampleMethod",  value = "Sample Method Annotation")
   public String getSampleField() {
      return sampleField;
   }
   public void setSampleField(String sampleField) {
      this.sampleField = sampleField;
   }
}
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation {
   public String name();
   public String value();
}

编译并运行上面的程序,这将产生以下结果

-name: getSampleMethod

value: Sample Method Annotation

getInterfaces()

能够获得这个对象所实现的接口

配置多数据源并实现Druid自动切换

Spring Boot配置多数据源

配置yml文件

这里并没有对spring.datasource配置数据源,因为增加新数据源后,系统会覆盖由spring.datasource自动配置的内容。

这里自定义了两个数据源spring.datasource.cmmi和spring.datasource.zentao

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    base:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      initialize: true #指定初始化数据源,是否用data.sql来初始化,默认: true
      name: cmmi
      url: jdbc:mysql://127.0.0.1:3306/cmmi?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull
      username: root
      password: root
    zentao:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      initialize: true
      name: zentaopro
      url: jdbc:mysql://127.0.0.1:3306/zentaopro?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull
      username: root
      password: root

主数据源配置

注意,配置类需要对DataSource、DataSourceTransactionManager、SqlSessionFactory 、SqlSessionTemplate四个数据项进行配置;DataSource类型需要引入javax.sql.DataSource;当系统中有多个数据源时,必须有一个数据源为主数据源,使用@Primary修饰。

@MapperScan对指定dao包建立映射,确保在多个数据源下,自动选择合适的数据源,而在service层里不需要做特殊说明。

@Configuration
@MapperScan(basePackages = "cmmi.dao.base", sqlSessionTemplateRef = "baseSqlSessionTemplate")
public class BaseDataSourceConfig {
    @Bean(name = "baseDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.base")
    @Primary
    public DataSource setDataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean(name = "baseTransactionManager")
    @Primary
    public DataSourceTransactionManager setTransactionManager(@Qualifier("baseDataSource") DataSource dataSource) {
        return new DruidDataSource();
    }
    @Bean(name = "baseSqlSessionFactory")
    @Primary
    public SqlSessionFactory setSqlSessionFactory(@Qualifier("baseDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/base/*.xml"));
        return bean.getObject();
    }
    @Bean(name = "baseSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate setSqlSessionTemplate(@Qualifier("baseSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

从数据源配置

@Configuration
@MapperScan(basePackages = "cmmi.dao.zentao", sqlSessionTemplateRef = "zentaoSqlSessionTemplate")
public class ZentaoDataSourceConfig {
    @Bean(name = "zentaoDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.zentao")
    public DataSource setDataSource() {
        return new DruidDataSource();
    }
    @Bean(name = "zentaoTransactionManager")
    public DataSourceTransactionManager setTransactionManager(@Qualifier("zentaoDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    @Bean(name = "zentaoSqlSessionFactory")
    public SqlSessionFactory setSqlSessionFactory(@Qualifier("zentaoDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/zentao/*.xml"));
        return bean.getObject();
    }
    @Bean(name = "zentaoSqlSessionTemplate")
    public SqlSessionTemplate setSqlSessionTemplate(@Qualifier("zentaoSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

使用dao

这里只需要正常使用dao就可以了,spring会根据数据源配置的映射自动选择相应数据源,而不需要在service做特殊说明。

@Service
public class TestService {
    private final ZtUserMapper ztUserMapper;
    private final LevelDic levelDic;
    @Autowired
    public TestService(ZtUserMapper ztUserMapper, LevelDic levelDic) {
        this.ztUserMapper = ztUserMapper;
        this.levelDic = levelDic;
    }
    public void test() {
        ztUserMapper.selectByPrimaryKey(1);
        levelDic.setDicId(new Integer(1).byteValue());
    }
}

日志

o.a.c.c.C.[Tomcat].[localhost].[/cmmi] : Initializing Spring FrameworkServlet ‘dispatcherServlet'
o.s.web.servlet.DispatcherServlet : FrameworkServlet ‘dispatcherServlet': initialization started
o.s.web.servlet.DispatcherServlet : FrameworkServlet ‘dispatcherServlet': initialization completed in 23 ms
com.alibaba.druid.pool.DruidDataSource : {dataSource-1,cmmi} inited
com.alibaba.druid.pool.DruidDataSource : {dataSource-2,zentaopro} inited

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring实现动态切换多数据源的解决方案

    前言 Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性.而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据. Spring2.x以后的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来.Client提供选择所需的上下文

  • spring boot多数据源动态切换代码实例

    这篇文章主要介绍了spring boot多数据源动态切换代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 当项目中存在多数据源时,就涉及到数据源的动态切换,通过研究,特此记录一下. 1.maven依赖 <!--数据库连接--> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> &

  • Spring整合多数据源实现动态切换的实例讲解

    在实际项目中时常需要连接多个数据库,而且不同的业务需求在实现过程当中往往需要访问不同的数据库. jdbc.properties配置文件,配置多个dataSource ##########################MySQL##################################### hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect connection.driver_class=com.mysql.jdbc.

  • Spring配置多个数据源并实现动态切换示例

    1.配置两个不同的数据源,如下(由于项目使用的是druid数据库连接,配置可以会复杂点比较): <!-- 数据源配置1 --> <bean id="testDataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name=&quo

  • Spring Boot 动态数据源示例(多数据源自动切换)

    本文实现案例场景: 某系统除了需要从自己的主要数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库,要求可以在任何方法上可以灵活指定具体要操作的数据库. 为了在开发中以最简单的方法使用,本文基于注解和AOP的方法实现,在spring boot框架的项目中,添加本文实现的代码类后,只需要配置好数据源就可以直接通过注解使用,简单方便. 一配置二使用 1. 启动类注册动态数据源 2. 配置文件中配置多个数据源 3. 在需要的方法上使用注解指定数据源 1.在启动类添加 @Import({Dyna

  • druid多数据源配置+Datasurce动态切换方式

    目录 druid多数据源配置+Datasurce动态切换 AbstractRoutingDataSource 数据源动态切换 例子 配置多数据源并实现Druid自动切换 配置yml文件 主数据源配置 从数据源配置 使用dao 日志 druid多数据源配置+Datasurce动态切换 AbstractRoutingDataSource 数据源动态切换 spring 使用AbstractRoutingDataSource自定义动态数据源时的事务处理, 需要继承spring的AbstractRouti

  • 详解Spring Boot整合Mybatis实现 Druid多数据源配置

    一.多数据源的应用场景 目前,业界流行的数据操作框架是 Mybatis,那 Druid 是什么呢? Druid 是 Java 的数据库连接池组件.Druid 能够提供强大的监控和扩展功能.比如可以监控 SQL ,在监控业务可以查询慢查询 SQL 列表等.Druid 核心主要包括三部分: 1. DruidDriver 代理 Driver,能够提供基于 Filter-Chain 模式的插件体系. 2. DruidDataSource 高效可管理的数据库连接池 3. SQLParser 当业务数据量达

  • 详解Android的Splash启动图的两种动态切换方式

    冷启动的时候因为要考虑网路原因,默认显示一张本地图片. 热启动的时候会根据获取的启动图是否是新动态替换. 以下是实现动态替换的两种方式: Glide的缓存下载 Glide中的downloadOnly方法可实现图片的下载功能 图片下载 Observable.just(RetrofitHelper.API_BASE_URL + img) .subscribeOn(Schedulers.newThread()) .subscribe(new Action1<String>() { @Override

  • springboot中mybatis多数据源动态切换实现

    目录 多数据源配置引入 动态数据源路由实现 动态数据源切换使用 案例源码 在开发中,动态数据源配置还是用的比较多的,比如在多数据源使用方面,又或者是在多个DB之间切换方面.这里给出一个动态数据源的配置方案,两个DB均以mysql为例. 多数据源配置引入 mybatis和mysql在springboot中的引入这里就不在说了,不了解的可以参见springboot中mysql与mybatis的引入. 数据源配置如下: datasource: master: type: com.alibaba.dru

  • Spring+Mybatis动态切换数据源的方法

    功能需求是公司要做一个大的运营平台: 1.运营平台有自身的数据库,维护用户.角色.菜单.部分以及权限等基本功能. 2.运营平台还需要提供其他不同服务(服务A,服务B)的后台运营,服务A.服务B的数据库是独立的. 所以,运营平台至少要连三个库:运营库,A库,B库,并且希望达到针对每个功能请求能够自动切换到对应的数据源(我最终实现是针对Service的方法级别进行切换的,也可以实现针对每个DAO层的方法进行切换.我们系统的功能是相互之间比较独立的). 第一步:配置多数据源 1.定义数据源: 我采用的

  • Java中JFinal框架动态切换数据库的方法

    需求:需要根据企业ID切换对应的数据库,同时,后期可动态增加数据库配置 JFinal框架中对于对于多数据源配置有两种方式: 1.通过配置文件配置,有多少数据库就要配置多少,服务启动时加载所有数据库,缺点:不能动态增加数据库 2.只配置一个主数据库信息就可以了,其他数据库信息保存在表中,通过读取表数据加载数据库连接,优点:在数据表中增加数据库配置即可动态增加数据库连接. 本次主要介绍第2种方法: 一.新建数据表:保存数据库连接信息 配置表对应的实体类 public class DbDto { /*

  • 解决Druid动态数据源配置重复刷错误日志的问题

    Druid动态数据源配置 主要是继承AbstractRoutingDataSource再通过AOP来实现动态数据源切换. 下面给大家介绍Druid动态配置数据源重复刷错误日志问题,具体内容如下所示: 问题描述 功能需求: 使用druid数据库连接池实现 动态的配置数据源功能:IP.端口.用户名.密码都是用户页面手动输入,可以测试连接,保存数据源. 问题说明: 既然是用户自己配置的数据源,就无法避免输入错误,连接失败等情况. 预期情况:用户输入的配置错误,测试连接时,会返回连接失败的信息. 实际情

  • 使用springboot+druid双数据源动态配置操作

    目录 一.yml配置 二.动态切换数据源配置文件 1.数据源db1 2.数据源db2 三.多数据源的mapper包最好是分开 四.代码中调用 总结 进行动态切换,需要在类里面配置,顺便解决mybatis-plus自带代码无法使用问题,直接上代码: 一.yml配置 数据源可以都是oracle的也可以一个是oracle一个是mysql的. spring: datasource: druid: db-type: com.alibaba.druid.pool.DruidDataSource #多数据源1

  • SpringBoot多数据源配置并通过注解实现动态切换数据源

    目录 1. 环境准备 1.1 数据库准备 1.2 项目创建 2. ThreadLocal类介绍 3. AbstractRoutingDataSource类介绍 4. 具体实现 4.1 定义数据源枚举类 4.2 创建动态多数据源类 4.3 创建动态多数据源配置类 4.4 自定义注解用于指定数据源 4.5 AOP实现动态切换数据源 5. 测试使用 5.1 配置数据源 5.2 创建实体类 5.3 服务层代码 5.4 控制层代码 1. 环境准备 1.1 数据库准备 一个本地环境的MySQL数据库,数据库

随机推荐