当前位置:首页 > 通信资讯 > 正文

druid动态切换数据库(druiddatasource 动态数据源)

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

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

AbstractRoutingDataSource 数据源动态切换

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

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

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

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 /** * 保存当前线程数据源的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中

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 /** * 执行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上做处理比较好。

druid动态切换数据库(druiddatasource 动态数据源)

业务逻辑方法

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @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; } }

注解类

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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的配置文件

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 <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 使用动态数据源。

解释:

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

参数:

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

返回值:

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

例子

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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

?
1 getInterfaces()

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

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

Spring Boot配置多数据源

配置yml文件

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

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

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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层里不需要做特殊说明。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @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); } }

从数据源配置

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @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做特殊说明。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/fragrant_no1/article/details/87803041

如果您对该产品感兴趣,请填写办理(客服微信:xiaoxiongyidong)

为您推荐:

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。