spring读写分离 - 事务注解篇
Posted 空白-键
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring读写分离 - 事务注解篇相关的知识,希望对你有一定的参考价值。
思路参照 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();
以上是关于spring读写分离 - 事务注解篇的主要内容,如果未能解决你的问题,请参考以下文章