(1)对业务方法aop横切
如果我们从原来硬编码的事务管理系统中,将事务管理相关代码剥离出来,我们会怎么做?最直观的方法就是提供一个工具类,将事务管理逻辑几种到这个类中,而service实现类必须在执行前后调用工具类即可。
虽然这种方法可以让事务管理代码和业务代码分离,但是也是有很大缺陷的。实际上最好的方法就是使用aop,在执行前后进行事务管理操作。现在用拦截器实现,代码如下:
public class PrototypeTransactionInterceptor implements MethodInterceptor
{
private PlatformTransactionManager transactionManager;
public Object invoke(MethodInvocation invocation) throws Throwable
{
Method method = invocation.getMethod();
TransactionDefinition definition = getTransactionDefinitionByMethod(method);
TransactionStatus txStatus = transactionManager.getTransaction(definition);//开启事务
Object result = null;
try
{
result = invocation.proceed();
}
catch (Throwable t)
{
if (needRollbackOn(t))
{
transactionManager.rollback(txStatus);
}
else
{
transactionManager.commit(txStatus);
}
throw t;
}
transactionManager.commit(txStatus);
return result;
}
private boolean needRollbackOn(Throwable t)
{
// TODO ...
return false;
}
private TransactionDefinition getTransactionDefinitionByMethod(
Method method)
{
// TODO ...
return null;
}
public PlatformTransactionManager getTransactionManager()
{
return transactionManager;
}
public void setTransactionManager(PlatformTransactionManager transactionManager)
{
this.transactionManager = transactionManager;
}
}
(2)保证spring和DAO框架使用的是同一个Connection
1.为何必须保证在同一个连接中?
类比jdbc操作,都是先setAutoCommit(false),然后再进行CURD操作,最后commit
只有这样才能保证能够对事务中的操作进行回滚和提交
所以,必须要保证spring进行的事务操作和DAO框架使用的Connection是同一个
2.如何保证在同一个连接中?
1)先看一下spring中定义
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation"> <!-- name 为configLocation或s 不可为其他 -->
<value>sqlMap.xml</value> <!-- 不区分大小写,路径前可加'/' -->
</property>
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
可以看出spring的TransactionManager和ibatis使用的是同一个dataSource
有了这个动态代理类,spring就可以在getConnection方法前后进行一系列操作了
public class DatasourceHandler implements InvocationHandler {
private DataSource dataSource;
/**
* @param dataSource
*/
public DatasourceHandler(DataSource dataSource) {
super();
this.dataSource = dataSource;
}
/* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("getConnection")){
if(ResourceHolder.getResource(proxy)==null){
Connection connection = (Connection) method.invoke(this.dataSource, args);
ResourceHolder.addResource(proxy, connection);
}
return ResourceHolder.getResource(proxy);
}else{
return method.invoke(this.dataSource, args);
}
}
}
可以看出:先判断是否是getConnection方法,如果是,就判断ResourceHolder的resource中是否存在连接,如果不存在,就通过原来的DataSource(也就是spring中直接定义的那个)获取数据库连接,然后存放在ResourceHolder的resource中
那么再来看下ResourceHolder
public class ResourceHolder {
private static ThreadLocal<Map<Object,Object>> resources= new ThreadLocal<Map<Object,Object>>();
public static void addResource(Object key,Object value){
if(resources.get()==null){
resources.set(new HashMap<Object,Object>());
}
resources.get().put(key, value);
}
public static Object getResource(Object key){
return resources.get().get(key);
}
public static void clear(){
resources.remove();
}
}
可以看出resources实际上是ThreadLocal类型的,也就保证了一个线程中的connection肯定是同一个,这就保证了spring和DAO框架使用的是同一个connection,那么就可以进行事务管理了