spring動態數據源實現

 首先實現數據源路由

1.1 通過繼承AbstractRoutingDataSource類來實現數據源切換

 


 
  1. public class DynamicDataSource extends AbstractRoutingDataSource {

  2.  
  3. @Override

  4. protected Object determineCurrentLookupKey() {

  5. return DataSourceContextHolder.getDbType();

  6. }

  7.  
  8. }

該類中只有一個方法,該方法通過返回數據源的名稱動態的切換數據源

 

 

1.2 通過ThreadLocal 線程本地變量設置數據源名稱來保證線程安全

 


 
  1. public class DataSourceContextHolder {

  2. private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

  3.  
  4. public static void setDbType(String dbType) {

  5. contextHolder.set(dbType);

  6. }

  7.  
  8. public static String getDbType() {

  9. return ((String) contextHolder.get());

  10. }

  11.  
  12. public static void clearDbType() {

  13. contextHolder.remove();

  14. }

  15. }

ThreadLocal爲每個線程創建一個實例,使每個線程保持各自獨立的一個對象。通過contextHolder.set()將這個新創建的對象的引用保存到各線程的自己的一個map中,每個線程都有這樣一個map,執行contextHolder.get()時,各線程從自己的map中取出放進去的對象,因此取出來的是各自自己線程中的對象,ThreadLocal實例是作爲map的key來使用的。在本列子中就是爲了保證多個人可以同時訪問不同的數據源。

 

 

1.3 數據源名稱管理類

 


 
  1. public interface DataSourceName {

  2. static final String DATA_CHAT="test1"; //測試1庫

  3. static final String DATA_USER="test2"; //測試2庫

  4. }

1.3 配置XML文件

 

 


 
  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="

  3. http://www.springframework.org/schema/beans

  4. http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

  5. http://www.springframework.org/schema/tx

  6. http://www.springframework.org/schema/tx/spring-tx-4.3.xsd

  7. http://www.springframework.org/schema/aop

  8. http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

  9. <!--測試1-->

  10. <bean id="test1" class="com.alibaba.druid.pool.DruidDataSource"

  11. destroy-method="close">

  12. <property name="driverClassName" value="com.mysql.jdbc.Driver" />

  13. <property name="url" value="jdbc:mysql://XXXX/test1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true" />

  14. <property name="username" value="root" />

  15. <property name="password" value="isoftadmin" />

  16. <!-- 初始化連接大小 -->

  17. <property name="initialSize" value="1"></property>

  18. <!-- 連接池最大數量 -->

  19. <property name="maxActive" value="50"></property>

  20. <!-- 連接池最大空閒 -->

  21. <property name="maxIdle" value="2"></property>

  22. <!-- 連接池最小空閒 -->

  23. <property name="minIdle" value="1"></property>

  24. <!-- 獲取連接最大等待時間 -->

  25. <property name="maxWait" value="10000"></property>

  26. </bean>

  27. <!--測試2-->

  28. <bean id="test2" class="com.alibaba.druid.pool.DruidDataSource"

  29. destroy-method="close">

  30. <property name="driverClassName" value="com.mysql.jdbc.Driver" />

  31. <property name="url" value="jdbc:mysql://XXXX/test2?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true" />

  32. <property name="username" value="root" />

  33. <property name="password" value="isoftadmin" />

  34. <!-- 初始化連接大小 -->

  35. <property name="initialSize" value="1"></property>

  36. <!-- 連接池最大數量 -->

  37. <property name="maxActive" value="50"></property>

  38. <!-- 連接池最大空閒 -->

  39. <property name="maxIdle" value="2"></property>

  40. <!-- 連接池最小空閒 -->

  41. <property name="minIdle" value="1"></property>

  42. <!-- 獲取連接最大等待時間 -->

  43. <property name="maxWait" value="10000"></property>

  44. </bean>

  45. <!--數據源路由 DynamicDataSource就是自己編寫的數據源路由類 -->

  46. <bean id="dataSource" class="com.gcx.api.common.dataSource.DynamicDataSource">

  47. <property name="targetDataSources">

  48. <map key-type="java.lang.String">

  49. <entry key="test1" value-ref="test1"/>

  50. <entry key="test2" value-ref="test2"/>

  51. </map>

  52. </property>

  53. <!-- 默認數據源爲test1 -->

  54. <property name="defaultTargetDataSource" ref="test1"/>

  55. </bean>

  56. <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->

  57. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

  58. <property name="dataSource" ref="dataSource" />

  59. <!-- 自動掃描mapping.xml文件 -->

  60. <property name="mapperLocations" value="classpath:com/gcx/api/mapping/*.xml"></property>

  61. </bean>

  62. <!-- DAO接口所在包名,Spring會自動查找其下的類 -->

  63. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

  64. <property name="basePackage" value="com.gcx.api.dao" />

  65. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>

  66. </bean>

  67. <!-- (事務管理)transaction manager, use JtaTransactionManager for global tx -->

  68. <bean id="transactionManager"

  69. class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

  70. <property name="dataSource" ref="dataSource" />

  71. </bean>

  72. </beans>

 

編寫自定義註解

 

2.1 自定義註解類,用來在類或方法上設置數據源名稱


 
  1. @Target({ElementType.METHOD, ElementType.TYPE})

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. public @interface CustomDataSource {

  5. String value();

  6. }

 

編寫AOP

 

2.2 通過切面獲取註解的值

 


 
  1. @Component

  2. @Aspect

  3. public class DataAop {

  4.  
  5. @Before("execution(* com.gcx.api.service.impl.*.*(..))")

  6. public void switchDataSource(JoinPoint pjp) throws Throwable{

  7. Signature signature = pjp.getSignature();

  8. MethodSignature methodSignature = (MethodSignature)signature;

  9. Method targetMethod = methodSignature.getMethod();

  10. Method realMethod = pjp.getTarget().getClass().getDeclaredMethod(signature.getName(), targetMethod.getParameterTypes());

  11. //首先判斷方法級別

  12. CustomDataSource cds=realMethod.getAnnotation(CustomDataSource.class);

  13. if(cds==null){ //判斷類級別

  14. cds= AnnotationUtils.findAnnotation(realMethod.getDeclaringClass(), CustomDataSource.class);

  15. }

  16. if(cds==null){ //默認庫

  17. DataSourceContextHolder.setDbType(DataSourceName.DATA_CHAT);

  18. return;

  19. }

  20. String dataSourceName=cds.value(); //獲取註解的值

  21.  if(dataSourceName!=null&&!dataSourceName.equals("")) //通過數據源路由類切換數據源

  22. DataSourceContextHolder.setDbType(dataSourceName);

  23. }

  24.  
  25. }

該切面只攔截了Service實現層,所有註解需要寫在Service實現層的類或方法中。切面會在方法或類執行之前優先獲取方法上的註解,如果方法上沒有,則會去類上找,如果類上也沒有則會使用默認庫。

 

 在項目中使用

3.1 在Service實現層使用自定義註解切換數據源


 
  1. @CustomDataSource(DataSourceName.DATA_USER) //使用自定義註解切換數據源

  2. @Service

  3. public class UserServiceImpl implements UserService {

  4.  
  5. @Autowired

  6. private UserMapper userDao;

  7.  
  8. // 查詢所有記錄

  9. public MyResult findAllRecords(User record) throws Exception{

  10. List<User> list = userDao.findAllList(record);

  11. int count = (int) userDao.findAllListCount(record);

  12. return MyResult.ok(count, list);

  13. }

  14.  
  15.  //添加記錄

  16. @CustomDataSource(DataSourceName.DATA_CHAT)

  17. public MyResult addRecord(User record) throws Exception{

  18. // UUID主鍵

  19. long nanoTime = System.nanoTime();

  20. record.setUserId(new BigDecimal(nanoTime));

  21. int i = userDao.insertSelective(record);

  22. return MyResult.ok(i, "新增");

  23. }

  24.  
  25. // 刪除記錄

  26. public MyResult delRecord(String id) throws Exception{

  27. int i = userDao.deleteByPrimaryKey(id);

  28. return MyResult.ok(i, "刪除");

  29. }

  30. }

該類的find和del方法會使用test2庫,add方法會使用test1庫。
如果需要全部使用test1庫,則在方法或類上無需定義任何自定義註解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章