spring+spring mvc +mybatis+druid 实现数据库主从分离

来源:互联网 发布:php location 跳转 编辑:程序博客网 时间:2024/06/06 13:04

本文是基于:spring+spring mvc +mybatis+druid为基础框架, 实现mysql数据库主从分离.

mysql 主从配置(超简单)http://369369.blog.51cto.com/319630/790921/


第一步:基于java annotation(注解)并通过spring aop 实现动态数据源动态选择

[html] view plain copy
  1. package com.wlsq.util;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Target;  
  5. import java.lang.annotation.Retention;  
  6. import java.lang.annotation.RetentionPolicy;  
  7. /**  
  8.  * RUNTIME  
  9.  * 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。  
  10.  * @author yangGuang  
  11.  *  
  12.  */  
  13. @Retention(RetentionPolicy.RUNTIME)  
  14. @Target(ElementType.METHOD)  
  15. public @interface DataSource {  
  16.     String value();  
  17. }  

第二步: 实现spring 提供 AbstractRoutingDataSource 类 实现数据源设置

spring  AbstractRoutingDataSource类介绍:http://www.cnblogs.com/surge/p/3582248.html

[html] view plain copy
  1. package com.wlsq.util;  
  2.   
  3. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
  4.   
  5. public class ChooseDataSource extends AbstractRoutingDataSource  {  
  6.     /**  
  7.      * 获取与数据源相关的key  
  8.      * 此key是Map<String,DataSource> resolvedDataSources 中与数据源绑定的key值  
  9.      * 在通过determineTargetDataSource获取目标数据源时使用  
  10.      */  
  11.     @Override  
  12.     protected Object determineCurrentLookupKey() {  
  13.         // TODO Auto-generated method stub  
  14.         return HandleDataSource.getDataSource();  
  15.     }  
  16.   
  17. }  

第三步:利用ThreadLocal 解决数据源设置 线程安全性问题
[html] view plain copy
  1. package com.wlsq.util;  
  2.   
  3. /**  
  4.  * 保存当前线程数据源的key  
  5.  * @author   
  6.  * @version 1.0  
  7.  *  
  8.  */  
  9. public class HandleDataSource {  
  10.     public static final ThreadLocal<String> holder = new ThreadLocal<String>();  
  11.       
  12.     /**  
  13.      * 绑定当前线程数据源路由的key     
  14.      * @param key  
  15.      */  
  16.     public static void putDataSource(String datasource) {  
  17.         holder.set(datasource);  
  18.     }  
  19.       
  20.     /**  
  21.      * 获取当前线程的数据源路由的key  
  22.      * @return  
  23.      */  
  24.     public static String getDataSource() {  
  25.         return holder.get();  
  26.     }      
  27. }  

第四步: 定义一个数据源切面类,通过aop 实现访问,在spring 文件中进行相关的配置工作。
[html] view plain copy
  1. package com.wlsq.util;  
  2.   
  3.   
  4. import java.lang.reflect.Method;  
  5. import org.aspectj.lang.JoinPoint;  
  6.   
  7. import org.aspectj.lang.reflect.MethodSignature;  
  8.   
  9.   
  10. /**  
  11.  * 执行dao方法之前的切面  
  12.  * 获取datasource对象之前往HandleDataSource中指定当前线程数据源路由的key  
  13.  * @author Administrator  
  14.  *  
  15.  */  
  16. public class DataSourceAspect {  
  17.       
  18.      
  19.     /**  
  20.      * 在dao层方法之前获取datasource对象之前在切面中指定当前线程数据源路由的key  
  21.      */  
  22.      public void before(JoinPoint point)  
  23.         {         
  24.               
  25.               
  26.              Object target = point.getTarget();  
  27.              System.out.println(target.toString());  
  28.              String method = point.getSignature().getName();  
  29.              System.out.println(method);  
  30.              Class<?>[] classz = target.getClass().getInterfaces();  
  31.              Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())  
  32.                      .getMethod().getParameterTypes();  
  33.              try {  
  34.                  Method m = classz[0].getMethod(method, parameterTypes);  
  35.                  System.out.println(m.getName());  
  36.                  if (m != null && m.isAnnotationPresent(DataSource.class)) {  
  37.                      DataSource data = m.getAnnotation(DataSource.class);  
  38.                      System.out.println("用户选择数据库库类型:"+data.value());  
  39.                      HandleDataSource.putDataSource(data.value());  
  40.                  }  
  41.                    
  42.              } catch (Exception e) {  
  43.                  e.printStackTrace();  
  44.              }  
  45.         }  
  46. }  

第五步:配置spring-mybatis.xml 配置文件
[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
  4.     xmlns:context="http://www.springframework.org/schema/context"  
  5.     xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"  
  6.     xmlns:aop="http://www.springframework.org/schema/aop"  
  7.     xsi:schemaLocation="http://www.springframework.org/schema/beans    
  8.                         http://www.springframework.org/schema/beans/spring-beans-3.1.xsd    
  9.                         http://www.springframework.org/schema/context    
  10.                         http://www.springframework.org/schema/context/spring-context-3.1.xsd    
  11.                         http://www.springframework.org/schema/tx  
  12.                         http://www.springframework.org/schema/tx/spring-tx-3.1.xsd  
  13.                         http://www.springframework.org/schema/aop   
  14.                         http://www.springframework.org/schema/aop/spring-aop-3.1.xsd  
  15.                         http://www.springframework.org/schema/mvc    
  16.                         http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">  
  17.   
  18.   
  19.     <!-- 开启事务注解驱动 -->   
  20.     <tx:annotation-driven  transaction-manager="transactionManager"/>  
  21.       
  22.     <context:component-scan base-package="com.wlsq">  
  23.         <context:exclude-filter type="annotation"  
  24.             expression="org.springframework.stereotype.Controller" />  
  25.     </context:component-scan>  
  26.   
  27.     <!-- 引入配置文件 -->  
  28.     <bean id="propertyConfigurer"  
  29.         class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  30.         <property name="location" value="classpath:jdbc.properties" />  
  31.     </bean>  
  32.   
  33.   
  34.   
  35.     <bean id="readDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">     
  36.     <!-- 基本属性 url、user、password -->  
  37.       <property name="url" value="jdbc:mysql://localhost:3306/wlsq" />  
  38.       <property name="username" value="root" />  
  39.       <property name="password" value="123456" />  
  40.   
  41.       <!-- 配置初始化大小、最小、最大 -->  
  42.       <property name="initialSize" value="1" />  
  43.       <property name="minIdle" value="1" />   
  44.       <property name="maxActive" value="20" />  
  45.   
  46.       <!-- 配置获取连接等待超时的时间 -->  
  47.       <property name="maxWait" value="60000" />  
  48.   
  49.       <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->  
  50.       <property name="timeBetweenEvictionRunsMillis" value="60000" />  
  51.   
  52.       <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->  
  53.       <property name="minEvictableIdleTimeMillis" value="300000" />  
  54.   
  55.       <property name="validationQuery" value="SELECT 'x'" />  
  56.       <property name="testWhileIdle" value="true" />  
  57.       <property name="testOnBorrow" value="false" />  
  58.       <property name="testOnReturn" value="false" />  
  59.   
  60.       <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->  
  61.       <property name="poolPreparedStatements" value="true" />  
  62.       <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />  
  63.   
  64.       <!-- 配置监控统计拦截的filters -->  
  65.       <property name="filters" value="stat" />   
  66.     </bean>  
  67.       
  68.     <bean id="writeDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">    
  69.     <!-- 基本属性 url、user、password -->  
  70.       <property name="url" value="jdbc:mysql://localhost:3306/wlsqs" />  
  71.       <property name="username" value="root" />  
  72.       <property name="password" value="123456" />  
  73.   
  74.       <!-- 配置初始化大小、最小、最大 -->  
  75.       <property name="initialSize" value="1" />  
  76.       <property name="minIdle" value="1" />   
  77.       <property name="maxActive" value="20" />  
  78.   
  79.       <!-- 配置获取连接等待超时的时间 -->  
  80.       <property name="maxWait" value="60000" />  
  81.   
  82.       <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->  
  83.       <property name="timeBetweenEvictionRunsMillis" value="60000" />  
  84.   
  85.       <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->  
  86.       <property name="minEvictableIdleTimeMillis" value="300000" />  
  87.   
  88.       <property name="validationQuery" value="SELECT 'x'" />  
  89.       <property name="testWhileIdle" value="true" />  
  90.       <property name="testOnBorrow" value="false" />  
  91.       <property name="testOnReturn" value="false" />  
  92.   
  93.       <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->  
  94.       <property name="poolPreparedStatements" value="true" />  
  95.       <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />  
  96.   
  97.       <!-- 配置监控统计拦截的filters -->  
  98.       <property name="filters" value="stat" />   
  99.     </bean>  
  100.   
  101.     <bean id="dataSource" class="com.wlsq.util.ChooseDataSource">  
  102.         <property name="targetDataSources">      
  103.           <map key-type="java.lang.String">      
  104.               <!-- write -->    
  105.              <entry key="write" value-ref="writeDataSource"/>      
  106.              <!-- read -->    
  107.              <entry key="read" value-ref="readDataSource"/>      
  108.           </map>               
  109.         </property>   
  110.         <property name="defaultTargetDataSource" ref="readDataSource"/>      
  111.       
  112.     </bean>  
  113.   
  114.   
  115.   
  116.   
  117.     <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->  
  118.     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
  119.         <property name="dataSource" ref="dataSource" />  
  120.         <!-- 自动扫描mapping.xml文件 -->  
  121.         <property name="mapperLocations" value="classpath:com/wlsq/mapper/*.xml"></property>  
  122.     </bean>  
  123.   
  124.     <!-- DAO接口所在包名,Spring会自动查找其下的类 -->  
  125.     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
  126.         <property name="basePackage" value="com.wlsq.dao" />  
  127.         <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>  
  128.     </bean>  
  129.   
  130.     <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->  
  131.     <bean id="transactionManager"  
  132.         class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  133.         <property name="dataSource" ref="dataSource" />  
  134.     </bean>  
  135.       
  136.       
  137.      <!-- 为业务逻辑层的方法解析@DataSource注解  为当前线程的routeholder注入数据源key -->   
  138.     <bean id="dataSourceAspect" class="com.wlsq.util.DataSourceAspect" />    
  139.     <aop:config proxy-target-class="true">    
  140.         <aop:aspect id="dataSourceAspect" ref="dataSourceAspect" order="1">    
  141.             <aop:pointcut id="tx" expression="execution(* com.wlsq.service..*.*(..)) "/>    
  142.             <aop:before pointcut-ref="tx" method="before" />                
  143.         </aop:aspect>    
  144.     </aop:config>  
  145.       
  146.       
  147.   
  148.   
  149.   
  150.   
  151.   
  152.   
  153. </beans>  


第六步:使用@DataSource 标签,动态选择读写数据库
[html] view plain copy
  1. package com.wlsq.service;  
  2.   
  3. import java.util.List;  
  4. import java.util.Map;  
  5.   
  6. import org.apache.ibatis.annotations.Param;  
  7.   
  8. import com.wlsq.model.News;  
  9. import com.wlsq.model.User;  
  10. import com.wlsq.util.DataSource;  
  11.   
  12. public interface IUserMapperService {  
  13.       
  14.       
  15.     int deleteByPrimaryKey(Integer userid);  
  16.   
  17.     int insert(User record);  
  18.   
  19.     int insertSelective(User record);  
  20.   
  21.     User selectByPrimaryKey(Integer userid);  
  22.   
  23.     int updateByPrimaryKeySelective(User record);  
  24.   
  25.     int updateByPrimaryKey(User record);  
  26.       
  27.     List<User> selectByObject (User record);  
  28.       
  29.     @DataSource("read")  
  30.     public List<User> selectObject (User record);  
  31.       
  32.     @DataSource("write")  
  33.     public List<News> selectAllUsers(@Param("maps") Map<String, Object> maps);  
  34.       
  35.     @DataSource("write")  
  36.     public int selectCountUsers();  
  37. }  


学习总结:

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

解决办法:事务管理配置一定要配置在,往HandlerDataSource中注入数据源key之前.

第二个错误:spring aop 实现动态数据源选择,但有时存在数据源切换不及时,导致数据查询错误。

解决办法:调整aop 执行排序级别

[html] view plain copy
  1. <aop:aspect id="dataSourceAspect" ref="dataSourceAspect" order="1">   


转自:http://blog.csdn.net/zhouzhiwengang/article/details/51087920

其他参考:http://blog.csdn.net/jiawenbo89/article/details/51891053

http://blog.csdn.net/xtj332/article/details/43953699



阅读全文
0 0
原创粉丝点击