本文內容
- 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> T enhance(Class<T> 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<temp.length; i++) {
for (Class<? extends Interceptor> 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)從本地線程中移除連接
這就完成了事務操作