<%@ page contentType="text/html; charset=gb2312"%> 使用Annotation设计持久层
网站公告:   ◆北天JAVA技术网热情为java爱好者服务,本网内容包括JAVA(JSP、servlet、EJB、webservice、j2ee、javabean、应用服务器、JavaScript),数据库(MYSQL、SQL Server、Sybase、Oracle、DB2、数据库综合知识),设计研究(设计模式、Struts、Spring、Hibernate、设计框架、设计综合知识),WEB2.0新技术(主要介绍AJAX),以及各种技术的入门、实例、例子等等,欢迎各位多来坐坐!◆  诚邀各位JAVA爱好者加盟!◆  本网站内容丰富,更新快,保证每周20篇以上!  
加入收藏
设为首页
联系站长
承接项目
  相关资源:网站首页 | 免费培训学院 | 技术论坛 | JAVA聊天室 | 作家专栏 | 开发工具 | 认证考试 | 会员俱乐部
  JAVA技术初学者园地 | jsp与servlet | javascript | Java源代码 | EJB | web service | 应用服务器 | JAVA综合知识
  设计研究设计模式 | 设计框架 | Struts | Spring | Hibernate | 开源项目 | 面向对象设计 | 设计综合知识
  数 据 库MYSQL | SQL Server | Sybase | Oracle | DB2 | Informix | Access | 数据库综合知识
  其他资源:AJAX新技术 | 网站开发 | ERP软件 | OA办公软件 | 商业智能BI | 开发综合知识 | 承接项目 | 项目试用

 
 
使用Annotation设计持久层
     发布者: 发布时间:2007-03-07
    这篇文章的想法来自于过去的两篇文章:《设计自己的MVC框架》《设计模式之事务处理》
链接:
http://java.chinaitlab.com/model/532056.html
http://java.chinaitlab.com/model/533095.html


代码下载同样在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.    <resource-ref>
  2.         <description>DB Connection</description>
  3.         <res-ref-name>jdbctest</res-ref-name>
  4.         <res-type>javax.sql.DataSource</res-type>
  5.         <res-auth>Container</res-auth>
  6.     </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. package com.strutslet.db;
  2. import java.lang.annotation.Annotation;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. import java.sql.Connection;
  7. import java.sql.SQLException;
  8. import com.strutslet.exception.SystemException;
  9. public class TransactionWrapper {
  10.    
  11.     public static Object decorate(Object delegate) {
  12.         return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
  13.                 delegate.getClass().getInterfaces(), new XAWrapperHandler(
  14.                         delegate));
  15.     }
  16.     static final class XAWrapperHandler implements InvocationHandler {
  17.         private final Object delegate;
  18.         XAWrapperHandler(Object delegate) {
  19.             // Cache the wrapped delegate, so we can pass method invocations
  20.             // to it.
  21.             this.delegate = delegate;
  22.         }
  23.         public Object invoke(Object proxy, Method method, Object[] args)
  24.                 throws Throwable {
  25.             Object result = null;
  26.             Connection con = ConnectionManager.getConnection();
  27.             //得到Transaction标注
  28.             Transaction transaction = method.getAnnotation(Transaction.class);
  29.             //如果不为空,说明代理对象调用的方法需要事务控制。
  30.             if (transaction != null) {
  31.                 // System.out.println("transaction.." + con.toString());
  32.                 // 得到事务隔离级别信息
  33.                 int level = transaction.level();
  34.                 try {
  35.                     if (con.getAutoCommit())
  36.                         con.setAutoCommit(false);
  37.                     //设置事务隔离级别
  38.                     con.setTransactionIsolation(level);
  39.                     //调用原始对象的业务方法
  40.                     result = method.invoke(delegate, args);
  41.                     con.commit();
  42.                     con.setAutoCommit(true);
  43.                 } catch (SQLException se) {
  44.                     // Rollback exception will be thrown by the invoke method
  45.                     con.rollback();
  46.                     con.setAutoCommit(true);
  47.                     throw new SystemException(se);
  48.                 } catch (Exception e) {
  49.                     con.rollback();
  50.                     con.setAutoCommit(true);
  51.                     throw new SystemException(e);
  52.                 }
  53.             } else {
  54.                 result = method.invoke(delegate, args);
  55.             }
  56.             return result;
  57.         }
  58.     }
  59. }

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

  1. package com.strutslet.demo.service;
  2. import java.sql.SQLException;
  3. import com.strutslet.db.Transaction;
  4. import com.strutslet.demo.domain.AdminUser;
  5. public interface UserManager {
  6.     //查询,不需要事务控制
  7.     public boolean checkUser(String name, String password) throws SQLException;
  8.     //新增一个用户,需要事务控制,默认级别
  9.     @Transaction
  10.     public boolean addUser(AdminUser user) throws SQLException;
  11. }

 

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

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

(转载文章请保留出处:北天JAVA技术网(www.java114.com))
 
更多精彩文章:
试问:动态语言是否将会挽救Swing?
JSP牵手JSF打造完美的Web应用
Java的中文编程与配置心得
用JAVA实现线程等待提示框
SOA的发展历史与标准规范
二十分钟Ruby入门
 
最近评论:
        
你曾悄悄的来过!
wow gold,wow gold,wow gold,ffxi gil max(3571)
        
冰封的往事!
wow power leveling,wow gold,wow power leveling,wow gold max(6025)
        
冰封的往事!
wow power leveling,wow gold,WoW Gold,wow gold max(3337)
        
冰封的往事!
wow power leveling,wow gold,WoW Gold,wow gold max(1671)
        
冰封的往事!
wow power leveling,wow gold,WoW Gold,wow gold max(2860)
        
飞舞的传奇!
传世私服,传世私服.传奇世界私服传奇世界私服,传世私服传世私服, 传奇世界私服传奇世界私服.传奇私服传奇私服. max(3288)
        
标 题:   
内 容:   
 
                                  
 
免责声明:该文章由网友发表,如果对您造成侵权,请联系站长

首页 - 承接项目 - 网站地图 - 联系我们 -
版权所有北天JAVA技术工作室 ICP证号:粤ICP备06079815号