好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

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上做处理比较好。

业务逻辑方法

?

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测试数据mons.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测试数据mons.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

查看更多关于druid多数据源配置+Datasurce动态切换方式的详细内容...

  阅读:24次