本文内容
- jFinal怎样使用数据库事务
- jFinal的事务是怎么实现的
- 在需要事务的方法上添加注解@Before(Tx.class),代码如下
@Before(Tx.class)
public boolean updateOrder(Order order){
.......
}
并且在实例化此类的时候实用动态代理来增强,代码如下
private OrderDao orderDao = Enhancer.enhance(OrderDao.class);
- 从Enhancer类的enhance方法开始,enhance方法的代码如下
public static T enhance(Class targetClass) {
return (T)net.sf.cglib.proxy.Enhancer.create(targetClass, new Callback());
}
使用cglib的动态代理功能来增强目标类,通过回调Callback类中的intercept方法来调用被代理类的方法,intercept代码如下
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if (excludedMethodName.contains(method.getName())) {
if (method.getName().equals("finalize"))
return methodProxy.invokeSuper(target, args);
return this.injectTarget != null ? methodProxy.invoke(this.injectTarget, args) : methodProxy.invokeSuper(target, args);
}
if (this.injectTarget != null) {
target = this.injectTarget;
Interceptor[] finalInters = InterceptorBuilder.build(injectInters, target.getClass(), method);
Invocation invocation = new Invocation(target, method, args, methodProxy, finalInters);
invocation.useInjectTarget = true;
invocation.invoke();
return invocation.getReturnValue();
}
else {
Interceptor[] finalInters = InterceptorBuilder.build(injectInters, target.getClass(), method);
Invocation invocation = new Invocation(target, method, args, methodProxy, finalInters);
invocation.useInjectTarget = false;
invocation.invoke();
return invocation.getReturnValue();
}
}
首先判断方法名为“finalize”的逻辑不看,跳过
看下面if中的代码
if (this.injectTarget != null) {
target = this.injectTarget;
// 获取到所有的拦截器集合
Interceptor[] finalInters = InterceptorBuilder.build(injectInters, target.getClass(), method);
// 将拦截器交给Invocation
Invocation invocation = new Invocation(target, method, args, methodProxy, finalInters);
invocation.useInjectTarget = true;
// 然后调用
invocation.invoke();
return invocation.getReturnValue();
}
第一步获取到所有的拦截器集合,InterceptorBuilder.build的代码如下
public static Interceptor[] build(Interceptor[] injectInters, Class> targetClass, Method method) {
Interceptor[] methodInters = createInterceptors(method.getAnnotation(Before.class));
// no Clear annotation
Clear clear = method.getAnnotation(Clear.class);
if (clear == null) {
Interceptor[] classInters = createInterceptors(targetClass.getAnnotation(Before.class));
Interceptor[] result = new Interceptor[globalInters.length + injectInters.length + classInters.length + methodInters.length];
int index = 0;
for (Interceptor inter : globalInters)
result[index++] = inter;
for (Interceptor inter : injectInters)
result[index++] = inter;
for (Interceptor inter : classInters)
result[index++] = inter;
for (Interceptor inter : methodInters)
result[index++] = inter;
return result;
}
// Clear annotation without parameter
Class extends Interceptor>[] clearInters = clear.value();
if (clearInters.length == 0)
return methodInters;
// Clear annotation with parameter
Interceptor[] classInters = createInterceptors(targetClass.getAnnotation(Before.class));
Interceptor[] temp = new Interceptor[globalInters.length + injectInters.length + classInters.length];
int index = 0;
for (Interceptor inter : globalInters)
temp[index++] = inter;
for (Interceptor inter : injectInters)
temp[index++] = inter;
for (Interceptor inter : classInters)
temp[index++] = inter;
int removeCount = 0;
for (int i=0; i ci : clearInters) {
if (temp[i].getClass() == ci) {
temp[i] = null;
removeCount++;
break;
}
}
}
Interceptor[] result = new Interceptor[temp.length + methodInters.length - removeCount];
index = 0;
for (Interceptor inter : temp)
if (inter != null)
result[index++] = inter;
for (Interceptor inter : methodInters)
result[index++] = inter;
return result;
}
第二步将拦截器交给Invocation
第三步调用invocation.invoke(),看invoke的代码
public void invoke() {
// 递归调用拦截器
if (index < inters.length) {
// 将拦截器实例传入一下拦截器
inters[index++].intercept(this);
}
else if (index++ == inters.length) { // index++ ensure invoke action only one time
try {
// Invoke the action
if (action != null) {
// 调用到具体Controller子类的方法
returnValue = action.getMethod().invoke(target, args);
}
// Invoke the method
else {
if (useInjectTarget){
returnValue = methodProxy.invoke(target, args);
}
else{
returnValue = methodProxy.invokeSuper(target, args);
}
}
}
catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
throw t instanceof RuntimeException ? (RuntimeException)t : new RuntimeException(e);
}
catch (RuntimeException e) {
throw e;
}
catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
递归调用拦截器,并把自己的实例传入进去。
最终会调用到Tx类中的intercept方法,因为Tx类就是一个拦截器,代码如下
public void intercept(Invocation inv) {
Config config = getConfigWithTxConfig(inv);
if (config == null)
config = DbKit.getConfig();
Connection conn = config.getThreadLocalConnection();
if (conn != null) { // Nested transaction support
try {
if (conn.getTransactionIsolation() < getTransactionLevel(config))
conn.setTransactionIsolation(getTransactionLevel(config));
inv.invoke();
return ;
} catch (SQLException e) {
throw new ActiveRecordException(e);
}
}
Boolean autoCommit = null;
try {
conn = config.getConnection();
autoCommit = conn.getAutoCommit();
config.setThreadLocalConnection(conn);
conn.setTransactionIsolation(getTransactionLevel(config)); // conn.setTransactionIsolation(transactionLevel);
conn.setAutoCommit(false);
inv.invoke();
conn.commit();
} catch (NestedTransactionHelpException e) {
if (conn != null) try {conn.rollback();} catch (Exception e1) {e1.printStackTrace();}
} catch (Throwable t) {
if (conn != null) try {conn.rollback();} catch (Exception e1) {e1.printStackTrace();}
throw t instanceof RuntimeException ? (RuntimeException)t : new ActiveRecordException(t);
}
finally {
try {
if (conn != null) {
if (autoCommit != null)
conn.setAutoCommit(autoCommit);
conn.close();
}
} catch (Throwable t) {
t.printStackTrace(); // can not throw exception here, otherwise the more important exception in previous catch block can not be thrown
}
finally {
config.removeThreadLocalConnection(); // prevent memory leak
}
}
}
1)从config中获取连接对象conn,保留连接事务的开关
2)把conn连接对象交给本地线程保管,确保下游数据库操作获取的conn对象是同一个 3)配置事务级别
4)设置手动提交事务
5)inv.invoke();这一行代码会调用下一个拦截器,最终调用到上面invoke() 方法内的这几行代码,因为useInjectTarget是false,所以看else中的代码
if (useInjectTarget){
returnValue = methodProxy.invoke(target, args);
}
else{
returnValue = methodProxy.invokeSuper(target, args);
}
这就执行了开头介绍的方法,
@Before(Tx.class)
public boolean updateOrder(Order order){
.......
}
方法内的数据库操作完成后,继续执行conn.commit();提交事务
6)如果有事务异常就回滚
7)还原连接的事务开关
8)从本地线程中移除连接
这就完成了事务操作