這篇文章的想法來自於過去的兩篇文章
《設計自己的MVC框架》《設計模式之事務處理》
鏈接
代碼下載同樣在的郵箱裡
用戶名 sharesources 密碼 javafans
本文只是學習性質的文章
我一開始的想法就是修改《設計模式之事務處理》
提供Annotation來提供事務支持
支持到方法級別
通過引入一個 @Transaction標注
如果被此標注的方法將自動享受事務處理
目的是學習下Annotation和加深下對聲明式事務處理的理解
Annotation是JDK
引入的新特性
現在越來越多的框架采用此特性來代替煩瑣的xml配置文件
比如hibernate
ejb
spring等
對Annotation不了解
請閱讀IBM網站上的文章
還有推薦javaeye的Annotation專欄 ///subject/Annotation
代碼的示例是一個簡單的用戶管理例子
首先
環境是mysql+jdk
+myeclipse
+tomcat
在mysql中建立一張表adminusers:
create table adminusers(id int() auto_increment not null primary key
name varchar() not null
password varchar() not null
user_type varchar());
然後在tomcat下建立一個數據源
把代碼中的strutslet
xml拷貝到tomcat安裝目錄下的 /conf/Catalina/localhost目錄裡
請自行修改文件中的數據庫用戶名和密碼
以及數據庫名稱
另外
把mysql的 jdbc驅動拷貝到tomcat安裝目錄下的common/lib目錄
這樣數據源就建好了
在web
xml中引用
<resourceref>
<description>DB Connection</description>
<resrefname>jdbctest</resrefname>
<restype>javaxsqlDataSource</restype>
<resauth>Container</resauth>
</resourceref>
我的例子只是在《設計模式之事務處理》的基礎上改造的
在那篇文章裡
我講解了自己對聲明式事務處理的理解
並利用動態代理實現了一個 TransactionWrapper(事務包裝器)
通過業務代理工廠提供兩種版本的業務對象
經過事務包裝的和未經過事務包裝的
我們在默認情況下包裝業務對象中的所有方法
但實際情況是
業務對象中的很多方法不用跟數據庫打交道
它們根本不需要包裝在一個事務上下文中
這就引出了
我們為什麼不提供一種方式來配置哪些方法需要事務控制而哪些並不需要?甚至提供事務隔離級別的聲明?很自然的想法就是提供一個配置文件
類似spring式的事務聲明
既然JDK
已經引入Annotation
相比於配置文件的煩瑣和容易出錯
我們定義一個@Transaction的annotation來提供此功能
看下Transaction
java的代碼
package comstrutsletdb;
import javalangannotationDocumented;
import javalangannotationElementType;
import javalangannotationRetention;
import javalangannotationRetentionPolicy;
import javalangannotationTarget;
import javasqlConnection;
@Target(ElementTypeMETHOD)
@Retention(RetentionPolicyRUNTIME)
@Documented
public @interface Transaction {
//事務隔離級別默認為read_committed
public int level() default ConnectionTRANSACTION_READ_COMMITTED ;
}
@Transaction 標注只有一個屬性levellevel表示事務的隔離級別默認為Read_Committed(也是一般JDBC驅動的默認級別JDBC驅動默認級別一般於數據庫的隔離級別一致) @Target(ElementTypeMETHOD)表示此標注作用於方法級別 @Retention(RetentionPolicyRUNTIME)表示在運行時此標注的信息將被加載進JVM並可以通過Annotation的 API讀取我們在運行時讀取Annotation的信息根據隔離級別和被標注的方法名決定是否將業務對象的方法加進事務控制我們只要稍微修改下 TransactionWrapper:
//TransactionWrapperjava
package comstrutsletdb;
import javalangannotationAnnotation;
import javalangreflectInvocationHandler;
import javalangreflectMethod;
import javalangreflectProxy;
import javasqlConnection;
import javasqlSQLException;
import comstrutsletexceptionSystemException;
public class TransactionWrapper {
public static Object decorate(Object delegate) {
return ProxynewProxyInstance(delegategetClass()getClassLoader()
delegategetClass()getInterfaces() new XAWrapperHandler(
delegate));
}
static final class XAWrapperHandler implements InvocationHandler {
private final Object delegate;
XAWrapperHandler(Object delegate) {
// Cache the wrapped delegate so we can pass method invocations
// to it
thisdelegate = delegate;
}
public Object invoke(Object proxy Method method Object[] args)
throws Throwable {
Object result = null;
Connection con = ConnectionManagergetConnection();
//得到Transaction標注
Transaction transaction = methodgetAnnotation(Transactionclass);
//如果不為空說明代理對象調用的方法需要事務控制
if (transaction != null) {
// Systemoutprintln(transaction + contoString());
// 得到事務隔離級別信息
int level = transactionlevel();
try {
if (congetAutoCommit())
consetAutoCommit(false);
//設置事務隔離級別
consetTransactionIsolation(level);
//調用原始對象的業務方法
result = methodinvoke(delegate args);
mit();
consetAutoCommit(true);
} catch (SQLException se) {
// Rollback exception will be thrown by the invoke method
conrollback();
consetAutoCommit(true);
throw new SystemException(se);
} catch (Exception e) {
conrollback();
consetAutoCommit(true);
throw new SystemException(e);
}
} else {
result = methodinvoke(delegate args);
}
return result;
}
}
}
現在
看下我們的UserManager業務接口
請注意
我們是使用動態代理
只能代理接口
所以要把@Transaction標注是接口中的業務方法(與EJB
中的Remote
Local接口類似的道理):
package comstrutsletdemoservice;
import javasqlSQLException;
import comstrutsletdbTransaction;
import comstrutsletdemodomainAdminUser;
public interface UserManager {
//查詢不需要事務控制
public boolean checkUser(String name String password) throws SQLException;
//新增一個用戶需要事務控制默認級別
@Transaction
public boolean addUser(AdminUser user) throws SQLException;
}
要把addUser改成其他事務隔離級別(比如oracle的serializable級別)
稍微修改下
@Transaction(level=Connection
TRANSACTION_SERIALIZABLE)
public boolean addUser(AdminUser user) throws SQLException;
不准備詳細解釋例子的業務流程
不過是登錄和增加用戶兩個業務方法
看下就明白
閱讀本文前最好已經讀過開頭提過的兩篇文章
我相信代碼是最好的解釋
)
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26651.html