spring读写分离 - 事务注解篇
来源:互联网 发布:json格式怎么打开mac 编辑:程序博客网 时间:2024/05/17 07:53
思路参照 spring读写分离 - 事务配置篇(转) ,不过是基于@Transactional判断,所以每个需要事务的方法上都必须添加上这个注解,这里直接贴出代码:
配置文件:
多数据源配置:
<bean id="dataSource" class="com.lmiky.platform.database.datasource.DynamicDataSource"> <property name="readDataSources"> <list> <ref bean="readDataSource1" /> <ref bean="readDataSource2" /> </list> </property> <property name="writeDataSource" ref="writeDataSource" /> </bean>
数据源拦截器:
<bean id="dateSourceAspect" class="com.lmiky.platform.database.datasource.DateSourceAspect" /> <aop:config expose-proxy="true"> <aop:aspect ref="dateSourceAspect" order="0"> <aop:pointcut id="dateSourcePointcut" expression="execution(* com.lmiky..service.impl..*.*(..))"/> <aop:around pointcut-ref="dateSourcePointcut" method="determineReadOrWriteDB" /> </aop:aspect> </aop:config>
要保证让这个拦截在事务的拦截器之前,否则如果spring先拦截事务的话,就不会起效了。可以用order设的值来排序,或者把这个配置的代码放在跟事务配置的代码同一个页面,并且放在事务配置代码的前面,spring是按代码顺序来执行的。
其他的跟单个数据源配置一样。
java代码:
package com.lmiky.platform.database.datasource;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.transaction.annotation.Transactional;import java.lang.reflect.Method;import java.util.concurrent.ConcurrentHashMap;/** * 数据源切片 * * @author lmiky * @date 2015年9月7日 下午3:25:54 */public class DateSourceAspect { /** * 缓存 */ private static ConcurrentHashMap<String, Boolean> methodIsReadCache = new ConcurrentHashMap<>(); /** * 决策是否只读 * * @param pjp 织入点 * @return 方法执行结果 * @throws Throwable * @author lmiky * @date 2015年9月7日 下午3:45:27 */ public Object determineReadOrWriteDB(ProceedingJoinPoint pjp) throws Throwable { Method method = ((MethodSignature) pjp.getSignature()).getMethod(); Object target = pjp.getTarget(); String cacheKey = target.getClass().getName() + "." + method.getName(); Boolean isReadCacheValue = methodIsReadCache.get(cacheKey); if (isReadCacheValue == null) { // 重新获取方法,否则传递的是接口的方法信息 Method realMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes()); isReadCacheValue = isChoiceReadDB(realMethod); methodIsReadCache.put(cacheKey, isReadCacheValue); } if (isReadCacheValue) { DynamicDataSourceHolder.markRead(); } else { DynamicDataSourceHolder.markWrite(); } try { return pjp.proceed(); } finally { DynamicDataSourceHolder.reset(); } } /** * 判断是否只读方法 * * @param method 执行方法 * @return 当前方法是否只读 * @author lmiky * @date 2015年9月7日 下午3:45:10 */ private boolean isChoiceReadDB(Method method) { Transactional transactionalAnno = AnnotationUtils.findAnnotation(method, Transactional.class); if (transactionalAnno == null) { return true; } // 如果之前选择了写库,则现在还选择写库 if (DynamicDataSourceHolder.isChoiceWrite()) { return false; } if (transactionalAnno.readOnly()) { return true; } return false; }}数据源选择:
package com.lmiky.platform.database.datasource;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.concurrent.atomic.AtomicInteger;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * 动态数据源 * * @author lmiky * @date 2015年9月7日 下午2:01:31 */public class DynamicDataSource extends AbstractRoutingDataSource { private Object writeDataSource; private List<Object> readDataSources; private int readDataSourceSize = 0; private AtomicInteger readIndex = new AtomicInteger(0); /** * 数据源键名 */ private static final String DATASOURCE_KEY_WRITE = "write"; private static final String DATASOURCE_KEY_READ = "read"; /* (non-Javadoc) * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#afterPropertiesSet() */ @Override public void afterPropertiesSet() { if (this.writeDataSource == null) { throw new IllegalArgumentException("Property 'writeDataSource' is required"); } setDefaultTargetDataSource(writeDataSource); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DATASOURCE_KEY_WRITE, writeDataSource); if (this.readDataSources == null) { readDataSourceSize = 0; } else { for(int i=0; i<readDataSources.size(); i++) { targetDataSources.put(DATASOURCE_KEY_READ + i, readDataSources.get(i)); } readDataSourceSize = readDataSources.size(); } setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } /* * (non-Javadoc) * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey() */ @Override protected Object determineCurrentLookupKey() { if(DynamicDataSourceHolder.isChoiceNone() || DynamicDataSourceHolder.isChoiceWrite() || readDataSourceSize == 0) { return DATASOURCE_KEY_WRITE; } int index = readIndex.incrementAndGet() % readDataSourceSize; return DATASOURCE_KEY_READ + index; } /** * @return the writeDataSource */ public Object getWriteDataSource() { return writeDataSource; } /** * @param writeDataSource the writeDataSource to set */ public void setWriteDataSource(Object writeDataSource) { this.writeDataSource = writeDataSource; } /** * @return the readDataSources */ public List<Object> getReadDataSources() { return readDataSources; } /** * @param readDataSources the readDataSources to set */ public void setReadDataSources(List<Object> readDataSources) { this.readDataSources = readDataSources; }}
package com.lmiky.platform.database.datasource;/** * 数据源管理器 * * @author lmiky * @date 2015年9月7日 下午2:02:23 */public class DynamicDataSourceHolder { private static enum DataSourceType { write, read; } public static final ThreadLocal<DataSourceType> holder = new ThreadLocal<>(); /** * 数据源名称 */ public static final String DATASOURCE_WRITE = "write"; public static final String DATASOURCE_READ = "read"; /** * 标记为写数据源 * @author lmiky * @date 2015年9月9日 下午8:57:43 */ public static void markWrite() { holder.set(DataSourceType.write); } /** * 标记为读数据源 * @author lmiky * @date 2015年9月9日 下午8:57:43 */ public static void markRead() { holder.set(DataSourceType.read); } /** * 重置 * @author lmiky * @date 2015年9月9日 下午8:58:01 */ public static void reset() { holder.set(null); } /** * 是否还未设置数据源 * @author lmiky * @date 2015年9月9日 下午8:58:09 * @return */ public static boolean isChoiceNone() { return null == holder.get(); } /** * 当前是否选择了写数据源 * @author lmiky * @date 2015年9月9日 下午8:58:19 * @return */ public static boolean isChoiceWrite() { return DataSourceType.write == holder.get(); } /** * 当前是否选择了读数据源 * @author lmiky * @date 2015年9月9日 下午8:58:29 * @return */ public static boolean isChoiceRead() { return DataSourceType.read == holder.get(); }}
0 0
- spring读写分离 - 事务注解篇
- spring读写分离 - 事务配置篇(转)
- 数据库读写分离-spring事务配置篇(转)
- Spring AOP实现事务和主从读写分离
- Spring实现数据库读写分离/spring事务配置解释(Annotation/Spring AOP/Reflection)
- spring jpa 读写分离
- Spring 读写分离
- Spring读写分离技术
- spring 数据库读写分离
- Spring读写分离实战
- spring实现读写分离
- spring实现读写分离
- spring读写分离
- 注解方式实现读写分离
- spring boot学习7之mybatis+mysql读写分离(一写多读)+事务
- spring事务注解
- spring事务注解
- spring 事务注解
- 小小君的C语言第三课
- iOS开发系列--打造自己的“美图秀秀”
- 基本字符串压缩
- PID 控制从模拟到数字变换方法
- 残缺棋盘
- spring读写分离 - 事务注解篇
- OPENCV3.0+VS2013配置问题+cmake配置
- [optimization]Dogleg Method狗腿算法
- 第一篇
- 一句话了解Java回调机制的使用和功能
- Gamecenter 测试失败的解决方案
- [前端] 不定宽高居中对齐
- 数值优化(Numerical Optimization)学习系列-非线性方程(Nonlinear Equation)
- 树莓派编程wiringPi控制电子数码管显示循环0~9