赛迪网 > IT技术 Java > Java大图列表
  IT资讯搜索
 
IT产品搜索
[程序开发][网管世界][网络安全][数据库技术]
[操作系统][嘉宾聊天·在线访谈][活动集锦]
[精彩专题][Symantec专区][订阅IT技术周刊]
[开发论坛][网管论坛][安全论坛][数据库论坛]
[操作系统论坛][Sybase专区][IBM dW技术专区]
[病毒求助][病毒与漏洞播报][文档·源码下载]

使用Annotation设计持久层

发布时间:2007.12.27 12:47     来源:赛迪网    作者:baocl

本文只是学习性质的文章,我一开始的想法就是修改《设计模式之事务处理》,提供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:

    create table adminusers(id int(10) auto_increment not null primary key,
     name varchar(10) not null,
     password varchar(10) not null,
     user_type varchar(10));
 
    然后在tomcat下建立一个数据源,把代码中的strutslet.xml拷贝到tomcat安装目录下的 /conf/Catalina/localhost目录里,请自行修改文件中的数据库用户名和密码,以及数据库名称。另外,把mysql的 jdbc驱动拷贝到tomcat安装目录下的common/lib目录。这样数据源就建好了。在web.xml中引用:


   < resource-ref>
         < description>DB Connection< /description>
        < res-ref-name>jdbctest< /res-ref-name>
        < res-type>javax.sql.DataSource< /res-type>
        < res-auth>Container< /res-auth>
    < /resource-ref>
   
    我的例子只是在《设计模式之事务处理》的基础上改造的,在那篇文章里,我讲解了自己对声明式事务处理的理解,并利用动态代理实现了一个 TransactionWrapper(事务包装器),通过业务代理工厂提供两种版本的业务对象:经过事务包装的和未经过事务包装的。我们在默认情况下包装业务对象中的所有方法,但实际情况是,业务对象中的很多方法不用跟数据库打交道,它们根本不需要包装在一个事务上下文中,这就引出了,我们为什么不提供一种方式来配置哪些方法需要事务控制而哪些并不需要?甚至提供事务隔离级别的声明?很自然的想法就是提供一个配置文件,类似spring式的事务声明。既然JDK5已经引入Annotation,相比于配置文件的烦琐和容易出错,我们定义一个@Transaction的annotation来提供此功能。

    看下Transaction.java的代码:
 
    package com.strutslet.db;

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.sql.Connection;

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

//TransactionWrapper.java


package com.strutslet.db;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

import com.strutslet.exception.SystemException;

public class TransactionWrapper {

   
    public static Object decorate(Object delegate) {
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
                delegate.getClass().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.
            this.delegate = delegate;
        }

        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            Object result = null;
            Connection con = ConnectionManager.getConnection();
            //得到Transaction标注
            Transaction transaction = method.getAnnotation(Transaction.class);

            //如果不为空,说明代理对象调用的方法需要事务控制。
            if (transaction != null) {
                // System.out.println("transaction.." + con.toString());
                // 得到事务隔离级别信息
                int level = transaction.level();
                try {
                    if (con.getAutoCommit())
                        con.setAutoCommit(false);
                    //设置事务隔离级别
                    con.setTransactionIsolation(level);
                    //调用原始对象的业务方法
                    result = method.invoke(delegate, args);
                    con.commit();
                    con.setAutoCommit(true);
                } catch (SQLException se) {
                    // Rollback exception will be thrown by the invoke method
                    con.rollback();
                    con.setAutoCommit(true);
                    throw new SystemException(se);
                } catch (Exception e) {
                    con.rollback();
                    con.setAutoCommit(true);
                    throw new SystemException(e);
                }
            } else {
                result = method.invoke(delegate, args);
            }

            return result;
        }
    }
}
 
现在,看下我们的UserManager业务接口,请注意,我们是使用动态代理,只能代理接口,所以要把@Transaction标注是接口中的业务方法(与EJB3中的Remote,Local接口类似的道理):

      (责任编辑:包春林)


[ 发表评论 ] 字体[  ] [ 打印 ] [ 进入博客 ] [ 进入论坛 ]  [ 推荐给朋友 ]
  相关文章
· 程序人生--一个程序员应该注意的问题 (12-26) · 初学入门:一个完备的数据库连接池类 (12-26)
· Java的九大特点 能帮助你很快了解java (12-26) · 一篇不错的介绍Java Socket编程的文章 (12-26)
· 程序员在职场中很费解的话题-什么叫精通? (12-26) · Java多线程设计模式:wait/notify机制 (12-26)
· 基础:Java编写过程中安全问题解决指南 (12-26) · 如何在Jini,RMI和Applet中实现代码签名 (12-26)
· JAVA基础:讨论Java中的内部类和匿名类 (12-26) · 进阶:Java中用动态代理类实现记忆功能 (12-26)
  客户需求反馈表
* 姓  名:
更多资料  了解方案  认识厂商
* 单位名称:
* 联系电话:
* 电子邮件:
  赛迪推荐  
  手机·资费 ·新品·导购·评测·手机资费·宽带
手机搜索  诺基亚 N73 MOTO Z6
  IT产品 ·笔记本·台式机·服务器·打印·投影
IT产品搜索 
  IT技术 ·开发·网管·安全·数据库·操作系统
  信息化 ·热点·专题·访谈·周刊·方案案例
· 工信部“三定”公布 总编制731名设24司局
· 北京发电子商务监管意见 营利性网店须办照
· 直播 08中国城市信息化高峰论坛 案例点评
· 烽火网络校园解决方案 移民安置信息管理系统
  IT博客 ·曾剑秋·项立刚·Java学习·网管
  IT技术论坛 ·开发·网管·安全·数据库·系统