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

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

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

以上是 druid多数据源配置+Datasurce动态切换方式 的全部内容, 来源链接: utcz.com/p/248681.html

回到顶部