事務類本身就是一個攔截器,可以用注解的方式配置。方法內部的所有 DML 操作都將在本次事務之內。
配置代碼如下:
@Before(Tx.class)
public void savePost(){
//...
}
事務配置就是這麼簡單任性。
上述配置中為 savePost() 配置了事務也就是攔截器 Tx,當調用到 savePost() 的時候,是會進入到 Tx 的 intercept 方法中的:
主要代碼如下:
public void intercept(Invocation inv) {
Config config = getConfigWithTxConfig(inv);
if (config == null)
config = DbKit.getConfig();
Connection conn = config.getThreadLocalConnection();
// 下面這段支持嵌套事務,可以忽略不看
if (conn != null) {
try {
if (conn.getTransactionIsolation() < getTransactionLevel(config))
conn.setTransactionIsolation(getTransactionLevel(config));
inv.invoke();
return ;
} catch (SQLException e) {
throw new ActiveRecordException(e);
}
}
Boolean autoCommit = null;
try {
// 1. 建立數據庫連接
conn = config.getConnection();
autoCommit = conn.getAutoCommit();
config.setThreadLocalConnection(conn);
// 2. 設置事務隔離級別
conn.setTransactionIsolation(getTransactionLevel(config)); // conn.setTransactionIsolation(transactionLevel);
// 3. 設置事務手動提交
conn.setAutoCommit(false);
// 4. 反射機制調用 savePost()
inv.invoke();
// 5. 事務提交
conn.commit();
} catch (NestedTransactionHelpException e) {
if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);}
LogKit.logNothing(e);
} catch (Throwable t) {
// 6. 若有異常就回滾
if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);}
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) {
LogKit.error(t.getMessage(), t); // can not throw exception here, otherwise the more important exception in previous catch block can not be thrown
}
finally {
config.removeThreadLocalConnection(); // prevent memory leak
}
}
}
Tx.java 使用的是 JFinal 默認配置的事務隔離級別,是在 DbKit.java 中配置的
public static final int DEFAULT_TRANSACTION_LEVEL = Connection.TRANSACTION_REPEATABLE_READ;
JFinal 還有幾個攔截器,可以根據事務隔離級別的需求選用。
它們都繼承與 Tx.java,唯一不同的就是事務隔離級別。
以 TxReadCommitted 為例。
繼承 Tx.java,覆蓋了 getTransactionLevel 方法,返回常量值,這個常量就代表了事務隔離級別。
public class TxReadCommitted extends Tx {
/**
* A constant indicating that
* dirty reads are prevented; non-repeatable reads and phantom
* reads can occur. This level only prohibits a transaction
* from reading a row with uncommitted changes in it.
*/
private int TRANSACTION_READ_COMMITTED = 2;
@Override
protected int getTransactionLevel(com.jfinal.plugin.activerecord.Config config) {
return TRANSACTION_READ_COMMITTED;
}
}
這種實現方式並沒有使用攔截器。
跟蹤代碼最終追到 DbPro.java 如下代碼中:
boolean tx(Config config, int transactionLevel, IAtom atom) {
Connection conn = config.getThreadLocalConnection();
if (conn != null) { // Nested transaction support
try {
if (conn.getTransactionIsolation() < transactionLevel)
conn.setTransactionIsolation(transactionLevel);
boolean result = atom.run();
if (result)
return true;
throw new NestedTransactionHelpException("Notice the outer transaction that the nested transaction return false"); // important:can not return false
}
catch (SQLException e) {
throw new ActiveRecordException(e);
}
}
Boolean autoCommit = null;
try {
// 1. 建立數據庫連接
conn = config.getConnection();
autoCommit = conn.getAutoCommit();
config.setThreadLocalConnection(conn);
// 2. 設置事務隔離級別
conn.setTransactionIsolation(transactionLevel);
// 3. 設置事務手動提交
conn.setAutoCommit(false);
// 4. 所有 DML 操作是否都執行成功?
boolean result = atom.run();
// 5. 都成功:提交;不是都成功:回滾
if (result)
conn.commit();
else
conn.rollback();
return result;
} catch (NestedTransactionHelpException e) {
if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);}
LogKit.logNothing(e);
return false;
} catch (Throwable t) {
if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);}
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) {
LogKit.error(t.getMessage(), t); // can not throw exception here, otherwise the more important exception in previous catch block can not be thrown
} finally {
config.removeThreadLocalConnection(); // prevent memory leak
}
}
}
主要事務流程:
Jfinal學習之路---Controller使用 http://www.linuxidc.com/Linux/2014-07/104323.htm
JFinal開發8個常見問題 http://www.linuxidc.com/Linux/2015-02/113421.htm
JFinal的詳細介紹:請點這裡
JFinal的下載地址:請點這裡