基于springAop动态切换数据源实现读写分离
2017-10-19 13:43
507 查看
读写分离的好处:高并发互联网下减少数据库压力。详细请自行百度。
现在需求:读数据从test库中,写数据从test2中。根据调用方法的不同实现动态切换。
直接代码:
bean.xml:
HandleDataSource.java:
DynamicDataSource.java:
注解类:在方法上使用,为方法指定一个数据源
service.java:使用事务时,传播属性很重要,要不调用的时候会在一个数据源上添加事务,就不对了。事务内部调用另一个事务时,走代理类的方法。(aop失效情况,查看我另一篇帖子)
测试类;
(******)动态选择的切面:1.注解加到接口上,解析的时候根据接口上的注解解析。2.没接口情况下,注解直接加到实现方法上。我注释的地方是第一种情况,没注释的是加载实现类上的,也是我现在写的。
增强类:
测试:执行test02()没异常情况下,往test2表中插入一条数据,并读取test中的的数据。如果有异常10/0事务回滚,两条操作都不执行。回滚指定事务段我没试,可以把我另一帖子的回滚段代码拿出来应该可以。
注意点:1.引入事务是切面那个order(-1)要加,表示位于增强链的最前。2.事务操作时要注意设置广播机制,尤其一个方法中调用另一个方法的事务时。
*************静态切换我研究研究,就是绑定sqlSessionFactory***************************
求助:能不能帮我把
<!-- 动态数据源配置 -->
<bean id="dynamicDataSource" class="cn.rjx.spring.mutidatasource1.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- 指定lookupKey和与之对应的数据源 -->
<entry key="test" value-ref="dataSource1"></entry>
<entry key="test2" value-ref="dataSource2"></entry>
</map>
</property>
<!--默认的数据源 -->
<property name="defaultTargetDataSource" ref="dataSource1" />
</bean>
转换成DynamicDataSource中的@Configuration+@bean形式?
现在需求:读数据从test库中,写数据从test2中。根据调用方法的不同实现动态切换。
直接代码:
bean.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <co 4000 ntext:component-scan base-package="cn.rjx.spring.mutidatasource1"></context:component-scan> <!-- ===================两个数据源============================ --> <bean name="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="123456" /> </bean> <bean name="dataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="jdbc:mysql://localhost:3306/test2" /> <property name="username" value="root" /> <property name="password" value="123456"/> </bean> <!-- 动态数据源配置 --> <bean id="dynamicDataSource" class="cn.rjx.spring.mutidatasource1.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <!-- 指定lookupKey和与之对应的数据源 --> <entry key="test" value-ref="dataSource1"></entry> <entry key="test2" value-ref="dataSource2"></entry> </map> </property> <!--默认的数据源 --> <property name="defaultTargetDataSource" ref="dataSource1" /> </bean> <!-- ==============mybatis配置=========================================== --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dynamicDataSource" /> <property name="mapperLocations" value="classpath:cn/rjx/spring/mutidatasource1/*Mapper.xml" /> </bean> <!-- 配置扫描器 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 扫描me.gacl.dao这个包以及它的子包下的所有映射接口类 --> <property name="basePackage" value="cn.rjx.spring.mutidatasource1" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <!-- ==============mybatis事务配置=========================================== --> <!-- 1.1.配置mybaties的事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name="dataSource" ref="dynamicDataSource" /> </bean> <!-- 开启注解事务 下 --> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- 使用@aspectj 注解时要加上 或者使用@EnableAspectJAutoProxy --> <aop:aspectj-autoproxy expose-proxy="true" /> </beans>
HandleDataSource.java:
/** * 把当前请求的数据源(xml配置targetDataSources下的key)塞入到ThreadLocal中 */ public class HandleDataSource { public static final ThreadLocal<String> holder=new ThreadLocal<String>(); public static void putDataSource(String dataSource){ holder.set(dataSource); } /** * ThreadLocal里面拿出当前请求的数据源 */ public static String getDataSource(){ return holder.get(); } public static void clearDataSource() { holder.remove(); } }
DynamicDataSource.java:
/** * 数据源选择类:拿到动态切换的Key spring给你选择数据源 * @author Administrator * */ public class DynamicDataSource extends AbstractRoutingDataSource{ /** * 告诉spring使用哪个数据源 */ @Override protected Object determineCurrentLookupKey() { return HandleDataSource.getDataSource(); } }
注解类:在方法上使用,为方法指定一个数据源
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface TargetDataSource { String value() default ""; }
service.java:使用事务时,传播属性很重要,要不调用的时候会在一个数据源上添加事务,就不对了。事务内部调用另一个事务时,走代理类的方法。(aop失效情况,查看我另一篇帖子)
@Service public class ServiceImpl /*implements ServiceI*/ { @Autowired Dao dao; /** *方法执行前对数据源进行动态切换 * @return */ @TargetDataSource("test") @Transactional(propagation=Propagation.REQUIRES_NEW) public List<User> findUser(){ List<User> list= dao.findUser(); int a=10/0; return list; } @TargetDataSource("test2") @Transactional public void addOrder(){ dao.addOrder(); /** * 当前数据源事务调用另一个数据源事务时,设置事务的传播属性,否则会在一个数据源上添加事务,那就不对了。 */ ServiceImpl service=(ServiceImpl)AopContext.currentProxy(); System.out.println(service.findUser()); } }
测试类;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:cn/rjx/spring/mutidatasource1/bean.xml"}) //加载配置文件 @Component public class Test01 { Logger logger=LoggerFactory.getLogger(ServiceImpl.class); @Resource ServiceImpl serviceImpl; @Test public void test01(){ logger.info("#################################"); List<User> list=serviceImpl.findUser(); System.out.println(list); } @Test public void test02(){ logger.info("---------------------------------------"); serviceImpl.addOrder(); } }
(******)动态选择的切面:1.注解加到接口上,解析的时候根据接口上的注解解析。2.没接口情况下,注解直接加到实现方法上。我注释的地方是第一种情况,没注释的是加载实现类上的,也是我现在写的。
增强类:
@Aspect @Order(-1)//多个aop配置时需要指定加载顺序(事务也是一个) -1为最先执行 @Component //@EnableAspectJAutoProxy public class DataSourceAspectJ /*implements MethodBeforeAdvice,AfterReturningAdvice*/ { @Pointcut(value = "execution(* cn.rjx.spring.mutidatasource1.ServiceImpl.*(..))") public void join(){ } @Before("join()") public void before(JoinPoint joinPoint){ System.out.println("before advice!"); //1.获取被代理类 Object target = joinPoint.getTarget(); //获取方法名称 String targetMethodName=joinPoint.getSignature().getName(); //2.拿到被代理类的接口 //Class<?>[] interfacezz=target.getClass().getInterfaces(); //3.拿到被代理的方法的入参 // Class<?>[] parameterTypes = ((MethodSignature)joinPoint.getSignature()) // .getMethod().getParameterTypes(); Method method = ((MethodSignature)joinPoint.getSignature()).getMethod(); if(method.isAnnotationPresent(TargetDataSource.class)){ TargetDataSource annotation = method.getAnnotation(TargetDataSource.class); HandleDataSource.putDataSource(annotation.value()); }else{ HandleDataSource.putDataSource("test"); } //4. 获取接口方法上的注解 // for(Class<?> intfzz:interfacezz){ // try { // Method method = intfzz.getMethod(targetMethodName, parameterTypes); // if(method!=null){ // if(method.isAnnotationPresent(TargetDataSource.class)){ // TargetDataSource annotation = method.getAnnotation(TargetDataSource.class); // HandleDataSource.putDataSource(annotation.value()); // }else{ // HandleDataSource.putDataSource("test"); // } // }else{ // continue; // } // // } catch (NoSuchMethodException e) { // e.printStackTrace(); // } catch (SecurityException e) { // e.printStackTrace(); // } // } } @AfterReturning("join()") public void after(){ System.out.println("after"); HandleDataSource.clearDataSource(); } }
测试:执行test02()没异常情况下,往test2表中插入一条数据,并读取test中的的数据。如果有异常10/0事务回滚,两条操作都不执行。回滚指定事务段我没试,可以把我另一帖子的回滚段代码拿出来应该可以。
注意点:1.引入事务是切面那个order(-1)要加,表示位于增强链的最前。2.事务操作时要注意设置广播机制,尤其一个方法中调用另一个方法的事务时。
*************静态切换我研究研究,就是绑定sqlSessionFactory***************************
求助:能不能帮我把
<!-- 动态数据源配置 -->
<bean id="dynamicDataSource" class="cn.rjx.spring.mutidatasource1.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- 指定lookupKey和与之对应的数据源 -->
<entry key="test" value-ref="dataSource1"></entry>
<entry key="test2" value-ref="dataSource2"></entry>
</map>
</property>
<!--默认的数据源 -->
<property name="defaultTargetDataSource" ref="dataSource1" />
</bean>
转换成DynamicDataSource中的@Configuration+@bean形式?
相关文章推荐
- 基于AOP动态切换数据源实现读写分离
- spring 基于注解动态切换数据源的实现
- 使用SpringAOP实现动态数据源读写分离
- SPRINGAOP实现基于注解的数据源动态切换(转)
- 基于spring+mybatis+atomikos+jta实现分布式事务(2)-动态切换数据源
- 继承AbstractRoutingDataSource再通过AOP实现动态数据源切换
- Spring(AbstractRoutingDataSource)实现动态数据源切换--转载
- Spring(AbstractRoutingDataSource)实现动态数据源切换--转载
- Spring学习总结(16)——Spring AOP实现执行数据库操作前根据业务来动态切换数据源
- 利用spring+ibatiS技术,在spring中配置多个数据源,并实现动态切换。
- spring轻松实现数据源动态切换
- Spring整合Mybatis实现动态数据源切换教程配置
- Spring(AbstractRoutingDataSource)实现动态数据源切换
- 利用AbstractRoutingDataSource实现动态数据源切换
- 基于Spring Boot实现Mybatis的多数据源切换和动态数据源加载
- 使用spring 实现真正多数据源的动态加载及动态切换
- 【原】继承AbstractRoutingDataSource再通过AOP实现动态数据源切换
- Spring实现动态数据源,支持动态添加、删除和设置权重及读写分离
- AbstractRoutingDataSource 实现动态切换数据源
- Spring+Mybatis多数据源配置(四)——AbstractRoutingDataSource实现数据源动态切换