Java资源网

| JAVA基础 | 环境配置 | JDBC | 线程技术 | Socket编程 | JavaMail | JAVA与XML | 设计模式 | 技术新闻 | Java认证 | 程序人生 软件下载
| JSP&Servlet | Spring | Struts | Hibernate | JBuilder | Eclipse | WebService | EJB技术 | J2ME开发 | 应用服务器 | JXTA | Ajax
Articles search文章搜索
   关键字:
   类 别:
       
New download 最新下载
· [组件]HTML Parser 1.5
· [教程]WebSphere Studio应用教程
· [组件]JDom 1.0
· [工具]Junit3.8.1
· [教程]EJB编程及J2EE系统架构和设计
· [教程]EJB教程
· [教程]J2EE Tutorial中文版
· [教程]Java编程思想2(英文)
· [教程]java编程思想(完整版)
· [教程]Java网络编程
New articles 最新文章
· 设计移动 Web 服务
· 解析XML的时候完全忽略DTD
· 理解XML Schema XML Schema 初步
· 标签库的深入研究
· 提升JSP应用程序的七大绝招
· 如何使用JDOM对XML文件进行操作
· 处理XML字符串中特殊字符
· 利用Digester把XML转换成为Java对象
· 使用WebService 和RMI远程协作
· 使用Axis开发Web Service程序
Articles top 热门文章
· Eclipse基础--plugin插件安装(6644)
· eclipse+tomcat+lomboz的安装配置说明(4774)
· Java程序员就业前景(4584)
· Windows下JAVA环境变量的设置祥解(3788)
· Tomcat下JSP、Servlet和JavaBean环境的配置(3716)
· 使用links方式安装Eclipse插件(3698)
· 一个老程序员的心理话(3533)
· linux下jdk的安装与配置(3459)
· 初学者入门:Structs中基本配置入门(3334)
· Eclipse 运行命令行参数大全(3084)
您的位置:首页>>设计模式>>使用Annotation设计持久层
使用Annotation设计持久层
2007-04-13   来源:www.javaresearch.org  作者:未知

这篇文章的想法来自于过去的两篇文章:《设计自己的MVC框架》《设计模式之事务处理》
链接:
http://www.javaresearch.org/article/59935.htm
http://www.javaresearch.org/article/59043.htm


代码下载同样在www.126.com的邮箱里,用户名 sharesources 密码 javafans

    本文只是学习性质的文章,我一开始的想法就是修改《设计模式之事务处理》,提供Annotation来提供事务支持,支持到方法级别。通过引入一个 @Transaction标注,如果被此标注的方法将自动享受事务处理。目的是学习下Annotation和加深下对声明式事务处理的理解。

    Annotation是JDK5引入的新特性,现在越来越多的框架采用此特性来代替烦琐的xml配置文件,比如hibernate,ejb3, spring等。对Annotation不了解,请阅读IBM网站上的文章,还有推荐javaeye的Annotation专栏:http: //www.javaeye.com/subject/Annotation

    代码的示例是一个简单的用户管理例子。

    首先,环境是mysql+jdk5+myeclipse5+tomcat5,在mysql中建立一张表adminusers:
  1.     create table adminusers(id int(10) auto_increment not null primary key,
  2.      name varchar(10) not null,
  3.      password varchar(10) not null,
  4.      user_type varchar(10));
 
    然后在tomcat下建立一个数据源,把代码中的strutslet.xml拷贝到tomcat安装目录下的 /conf/Catalina/localhost目录里,请自行修改文件中的数据库用户名和密码,以及数据库名称。另外,把mysql的 jdbc驱动拷贝到tomcat安装目录下的common/lib目录。这样数据源就建好了。在web.xml中引用:
  1.  
  2.    <resource-ref>
  3.         <description>DB Connection</description>
  4.         <res-ref-name>jdbctest</res-ref-name>
  5.         <res-type>javax.sql.DataSource</res-type>
  6.         <res-auth>Container</res-auth>
  7.     </resource-ref>
    
    我的例子只是在《设计模式之事务处理》的基础上改造的,在那篇文章里,我讲解了自己对声明式事务处理的理解,并利用动态代理实现了一个 TransactionWrapper(事务包装器),通过业务代理工厂提供两种版本的业务对象:经过事务包装的和未经过事务包装的。我们在默认情况下包装业务对象中的所有方法,但实际情况是,业务对象中的很多方法不用跟数据库打交道,它们根本不需要包装在一个事务上下文中,这就引出了,我们为什么不提供一种方式来配置哪些方法需要事务控制而哪些并不需要?甚至提供事务隔离级别的声明?很自然的想法就是提供一个配置文件,类似spring式的事务声明。既然JDK5已经引入Annotation,相比于配置文件的烦琐和容易出错,我们定义一个@Transaction的annotation来提供此功能。

    看下Transaction.java的代码:
 
  1.     package com.strutslet.db;
  2.     import java.lang.annotation.Documented;
  3.     import java.lang.annotation.ElementType;
  4.     import java.lang.annotation.Retention;
  5.     import java.lang.annotation.RetentionPolicy;
  6.     import java.lang.annotation.Target;
  7.     import java.sql.Connection;
  8.     @Target(ElementType.METHOD)
  9.     @Retention(RetentionPolicy.RUNTIME)
  10.     @Documented
  11.     public @interface Transaction {
  12.        //事务隔离级别,默认为read_committed
  13.        public int level() default Connection.TRANSACTION_READ_COMMITTED    ;
  14.     }
 
@Transaction 标注只有一个属性level,level表示事务的隔离级别,默认为Read_Committed(也是一般JDBC驱动的默认级别,JDBC驱动默认级别一般于数据库的隔离级别一致)。 @Target(ElementType.METHOD)表示此标注作用于方法级别, @Retention(RetentionPolicy.RUNTIME)表示在运行时,此标注的信息将被加载进JVM并可以通过Annotation的 API读取。我们在运行时读取Annotation的信息,根据隔离级别和被标注的方法名决定是否将业务对象的方法加进事务控制。我们只要稍微修改下 TransactionWrapper:

//TransactionWrapper.java
  1.  
  2. package com.strutslet.db;
  3. import java.lang.annotation.Annotation;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.Method;
  6. import java.lang.reflect.Proxy;
  7. import java.sql.Connection;
  8. import java.sql.SQLException;
  9. import com.strutslet.exception.SystemException;
  10. public class TransactionWrapper {
  11.    
  12.     public static Object decorate(Object delegate) {
  13.         return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
  14.                 delegate.getClass().getInterfaces(), new XAWrapperHandler(
  15.                         delegate));
  16.     }
  17.     static final class XAWrapperHandler implements InvocationHandler {
  18.         private final Object delegate;
  19.         XAWrapperHandler(Object delegate) {
  20.             // Cache the wrapped delegate, so we can pass method invocations
  21.             // to it.
  22.             this.delegate = delegate;
  23.         }
  24.         public Object invoke(Object proxy, Method method, Object[] args)
  25.                 throws Throwable {
  26.             Object result = null;
  27.             Connection con = ConnectionManager.getConnection();
  28.             //得到Transaction标注
  29.             Transaction transaction = method.getAnnotation(Transaction.class);
  30.             //如果不为空,说明代理对象调用的方法需要事务控制。
  31.             if (transaction != null) {
  32.                 // System.out.println("transaction.." + con.toString());
  33.                 // 得到事务隔离级别信息
  34.                 int level = transaction.level();
  35.                 try {
  36.                     if (con.getAutoCommit())
  37.                         con.setAutoCommit(false);
  38.                     //设置事务隔离级别
  39.                     con.setTransactionIsolation(level);
  40.                     //调用原始对象的业务方法
  41.                     result = method.invoke(delegate, args);
  42.                     con.commit();
  43.                     con.setAutoCommit(true);
  44.                 } catch (SQLException se) {
  45.                     // Rollback exception will be thrown by the invoke method
  46.                     con.rollback();
  47.                     con.setAutoCommit(true);
  48.                     throw new SystemException(se);
  49.                 } catch (Exception e) {
  50.                     con.rollback();
  51.                     con.setAutoCommit(true);
  52.                     throw new SystemException(e);
  53.                 }
  54.             } else {
  55.                 result = method.invoke(delegate, args);
  56.             }
  57.             return result;
  58.         }
  59.     }
  60. }
 
现在,看下我们的UserManager业务接口,请注意,我们是使用动态代理,只能代理接口,所以要把@Transaction标注是接口中的业务方法(与EJB3中的Remote,Local接口类似的道理):
  1.  
  2. package com.strutslet.demo.service;
  3. import java.sql.SQLException;
  4. import com.strutslet.db.Transaction;
  5. import com.strutslet.demo.domain.AdminUser;
  6. public interface UserManager {
  7.     //查询,不需要事务控制
  8.     public boolean checkUser(String name, String password) throws SQLException;
  9.     //新增一个用户,需要事务控制,默认级别
  10.     @Transaction
  11.     public boolean addUser(AdminUser user) throws SQLException;
  12. }
 

要把addUser改成其他事务隔离级别(比如oracle的serializable级别),稍微修改下:@Transaction(level=Connection.TRANSACTION_SERIALIZABLE)
public boolean addUser(AdminUser user) throws SQLException;

不准备详细解释例子的业务流程,不过是登录和增加用户两个业务方法,看下就明白。阅读本文前最好已经读过开头提过的两篇文章。我相信代码是最好的解释:)


  --相关文章--
· 面向对象编程,我的思想 (2007-04-13)
· 面向对象的思维方式 (2007-04-13)
· 通过Javanbsp;Swing看透MVC设计模式 (2007-04-13)
· 适配器模式(Adapternbsp;Pattern) (2007-04-13)
· 追MM与Java的23种设计模式 (2007-04-13)
· 责任链模式(Chainnbsp;ofnbsp;Responsibility) (2007-04-13)

版权所有©2005-2006 JAVA资源网 渝ICP备05007591号 虚拟主机 | 关于我们 | 联系方式 | 广告业务 | 网站地图 | 友情链接