`

Rapae 弱化DAO的一种方法

阅读更多

可怜的DAO层已经被各位大侠蹂躏得体肤完肤了,从范型DAO一直被蹂躏到现在只剩下一个可怜巴巴的接口,无不体现Java人追求敏捷开发的热情。其实,DAO层本来的作用就应该自从Hibernate一类优秀的ORM框架诞生之日起就应该消失灭迹了的。既然如此,那么我们就毁灭得更彻底一点。

 

下面是我对Service与DAO层整合的一些构想

 

约定优先于配置
一、Rapae代理接口标签定义

query属性:查询语句或者命名查询名称
  1. 不止由一个单词组成时:简单查询语句
  2. 由单个单词组成时:为命名查询,名称规则为[query的值]
  3. 为默认值""时:为命名查询,名称规则为[类名.方法名]
count属性:用于统计个数的查询语句或者命名查询名称,固定返回为一个Long型的数据
  1. 不止由一个单词组成时:简单查询语句
  2. 由单个单词组成时:为民命查询,名称规则为[count的值]
  3. 为默认值""时:自动生成count(*)语句
    • query为语句查询时:count自动生成为语句查询,规则为:select count(*) + query从第一个from开始到语句结束
    • query为命名查询时:count自动生成为命名查询,规则为:[query命名查询名称_count]
PS:当接口的方法没有Rapae注释的时候,按query与count均属于默认值情况处理








二、Pagination翻页注释

注释定义:
  1. FirstResult注释:方法级上的Annotation,它标记了分页查询时所要知道的第一条记录所在的位置。期待的类型为int
  2. MaxSize注释:方法级上的Annotation,它标记了分页查询时所要知道的每页最大查询记录数。期待的类型为int
  3. Total注释:方法级上的Annotation,它标记了分页查询后返回的总数统计。期待的类型为long
  4. Result注释:方法级上的Annotation,它标记了分页查询后返回的结果。期待的类型为java.util.Collection<E>
详细说明:
  1. 分页查询时:第一个参数必须是能同时提供有FirstResult和MaxSize注释方法的类,并且方法期待的返回类型都必须匹配,否则将会抛出异常。Rapae通过调用被标注的方法来进行分页查询。
  2. 分页查询返回时:返回的类必须同时提供有Result和Total注释的方法,参数个数为1,并且期待的类型都必须匹配,否则将会抛出异常。与此同时,若返回的类同时还能提供FirstResult、MaxSize注释的方法,方法的参数个数为1且为期待类型,那么Rapae在分页查询完成后将查询用到的 FirstResult、MaxSize值原封不动的通过标注的方法设回给返回类。






三、CRUD基本查询注释

注释定义:
  1. Create注释:格式 T [方法名] (T t);
  2. Read注释:格式 T [方法名] (java.io.Serializable pk);
  3. Update注释:格式 T [方法名] (T t);
  4. Delete注释:格式 T [方法名] (java.io.Serializable pk);
一旦被标注上了CRUD标签,则必须严格遵循标签所规定的格式,否则抛出异常。






四、查询行为方式与方法的返回类型、参数类型、参数个数以及方法名称之间的约定

查询行为方式
  1.  
    1. 执行查询
      • 返回值必须为void
    2. 查询多条记录(不翻页)
      • 返回值是java.util.Collection<E>的实现
    3. 查询多条记录(翻页)
      • 返回类必须同时提供标注有Result与Total的方法,并且参数个数为1,参数类型为标签所期待的类型
      • 返回类可以选择性的提供标注有FirstResult和MaxSize的方法,并且参数个数为1,参数类型为标签所期待的类型
      • 传入的第一个参数必须同时提供标注有FirstResult与MaxSize的方法,参数个数为0,返回类型为标签所期待的类型
    4. 查询单条记录
      • 返回值必须是一个对象
查询条件传递:
  1. 若是翻页查询,则参数个数至少为一个,且第一个参数必须为提供标标注有FirstResult与MaxSize的方法。
  2. 若查询条件是通过可变参或者Collection集合类进行传递的,则按顺序对查询条件进行设置
  3. 若查询条件是通过参数列表直接传递进来的,则按参数列表定义的顺序对查询条件进行设置
  4. 若查询条件是通过Map传递进来的,则可通过Map对参数的参数进行设置

 

首先我们先来看效果:

 

假设我们有这样一个Entity类

Java代码 复制代码
  1. import java.io.Serializable;   
  2. import java.util.Date;   
  3. import javax.persistence.*;   
  4.   
  5. @Entity  
  6. @Table(name = "ACCOUNT")   
  7. @SequenceGenerator(name = "ACCOUNT_SEQUENCE", sequenceName = "SEQ_ACCOUNT", initialValue = 1, allocationSize = 1)   
  8. public class Account implements Serializable {   
  9.   
  10.     private static final long serialVersionUID = 1L;   
  11.        
  12.        
  13.     @Id  
  14.     @Column(name = "ACCOUNT_ID")   
  15.     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ACCOUNT_SEQUENCE")   
  16.     private Long id;   
  17.        
  18.     @Column(name = "USERNAME",length=20, unique = true, nullable = false)   
  19.     private String username;   
  20.        
  21.     @Column(name = "PASSWORD",length=20, nullable = false)   
  22.     private String password;   
  23.        
  24.     @Column(name = "REG_TIME", nullable = false)   
  25.     @Temporal(TemporalType.TIMESTAMP)   
  26.     private Date registerTime;   
  27.        
  28.     Getter/Setter   
  29.   
  30.     public int hashCode() {   
  31.         return (this.id == null) ? 0 : this.id.hashCode();   
  32.     }   
  33.   
  34.     public boolean equals(Object object) {   
  35.         if (object instanceof Account) {   
  36.             final Account obj = (Account) object;   
  37.             return (this.id != null) ? this.id.equals(obj.id)   
  38.                     : (obj.id == null);   
  39.         }   
  40.         return false;   
  41.     }   
  42.   
  43. }  
import java.io.Serializable;
import java.util.Date;
import javax.persistence.*;

@Entity
@Table(name = "ACCOUNT")
@SequenceGenerator(name = "ACCOUNT_SEQUENCE", sequenceName = "SEQ_ACCOUNT", initialValue = 1, allocationSize = 1)
public class Account implements Serializable {

 private static final long serialVersionUID = 1L;
 
 
 @Id
 @Column(name = "ACCOUNT_ID")
 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ACCOUNT_SEQUENCE")
 private Long id;
 
 @Column(name = "USERNAME",length=20, unique = true, nullable = false)
 private String username;
 
 @Column(name = "PASSWORD",length=20, nullable = false)
 private String password;
 
 @Column(name = "REG_TIME", nullable = false)
 @Temporal(TemporalType.TIMESTAMP)
 private Date registerTime;
 
 Getter/Setter

 public int hashCode() {
  return (this.id == null) ? 0 : this.id.hashCode();
 }

 public boolean equals(Object object) {
  if (object instanceof Account) {
   final Account obj = (Account) object;
   return (this.id != null) ? this.id.equals(obj.id)
     : (obj.id == null);
  }
  return false;
 }

}

 

那么,我们在实际的开发过程中,只用写一个接口就能实现大部分的业务逻辑功能。

Java代码 复制代码
  1. import java.util.Collection;   
  2.   
  3. import org.springframework.transaction.annotation.Transactional;   
  4.   
  5. import test.com.rapae.model.Account;   
  6.   
  7. import com.javaforge.rapae.annotation.Rapae;   
  8. import com.javaforge.rapae.annotation.crud.*;   
  9. import com.javaforge.rapae.util.pagination.Page;   
  10. import com.javaforge.rapae.util.pagination.Pagination;   
  11.   
  12. @Transactional  
  13. public interface AccountService {   
  14.   
  15.     /**  
  16.      * 注册新帐号  
  17.      *   
  18.      * @param account  
  19.      * @return  
  20.      */  
  21.     @Create  
  22.     Account register(Account account);   
  23.   
  24.     /**  
  25.      * 通过ID获取帐号  
  26.      *   
  27.      * @param id  
  28.      * @return  
  29.      */  
  30.     @Read  
  31.     Account getById(Long id);   
  32.   
  33.     /**  
  34.      * 帐号修改  
  35.      *   
  36.      * @param account  
  37.      * @return  
  38.      */  
  39.     @Update  
  40.     Account update(Account account);   
  41.   
  42.     /**  
  43.      * 删除帐号  
  44.      *   
  45.      * @param id  
  46.      * @return  
  47.      */  
  48.     @Delete  
  49.     Account delete(Long id);   
  50.   
  51.     /**  
  52.      * 通过用户名获取帐号  
  53.      *   
  54.      * @param username  
  55.      * @return  
  56.      */  
  57.     @Rapae(query = "from Account where username = ?")   
  58.     Account getByUsername(String username);   
  59.   
  60.     /**  
  61.      * 帐号登录  
  62.      *   
  63.      * @param username  
  64.      * @param password  
  65.      * @return  
  66.      */  
  67.     @Rapae(query = "from Account where username = ? and password = ?")   
  68.     Account login(String username, String password);   
  69.        
  70.     /**  
  71.      * 不翻页获取所有的帐号  
  72.      * @return  
  73.      */  
  74.     @Rapae(query="from Account")   
  75.     Collection<Account> findAll();   
  76.        
  77.     /**  
  78.      * 翻页查询获取所有的帐号  
  79.      * @param username  
  80.      * @return  
  81.      */  
  82.     @Rapae(query="from Account")   
  83.     Pagination<Account> countPageFindAll(Page page);   
  84.        
  85.   
  86. }  
import java.util.Collection;

import org.springframework.transaction.annotation.Transactional;

import test.com.rapae.model.Account;

import com.javaforge.rapae.annotation.Rapae;
import com.javaforge.rapae.annotation.crud.*;
import com.javaforge.rapae.util.pagination.Page;
import com.javaforge.rapae.util.pagination.Pagination;

@Transactional
public interface AccountService {

 /**
  * 注册新帐号
  * 
  * @param account
  * @return
  */
 @Create
 Account register(Account account);

 /**
  * 通过ID获取帐号
  * 
  * @param id
  * @return
  */
 @Read
 Account getById(Long id);

 /**
  * 帐号修改
  * 
  * @param account
  * @return
  */
 @Update
 Account update(Account account);

 /**
  * 删除帐号
  * 
  * @param id
  * @return
  */
 @Delete
 Account delete(Long id);

 /**
  * 通过用户名获取帐号
  * 
  * @param username
  * @return
  */
 @Rapae(query = "from Account where username = ?")
 Account getByUsername(String username);

 /**
  * 帐号登录
  * 
  * @param username
  * @param password
  * @return
  */
 @Rapae(query = "from Account where username = ? and password = ?")
 Account login(String username, String password);
 
 /**
  * 不翻页获取所有的帐号
  * @return
  */
 @Rapae(query="from Account")
 Collection<Account> findAll();
 
 /**
  * 翻页查询获取所有的帐号
  * @param username
  * @return
  */
 @Rapae(query="from Account")
 Pagination<Account> countPageFindAll(Page page);
 

}

 

然后...没有然后了,接口上都已经写得非常清楚了,剩下的就交给Rapae去代理实现吧。

在Spring中这样配置

Xml代码 复制代码
  1. <bean id="rapaeProxyHandler"  
  2.     class="com.javaforge.rapae.handler.JPARapaeProxyHandler"  
  3.     scope="prototype">  
  4.     <property name="entityManagerFactory"  
  5.         ref="entityManagerFactory" />  
  6. </bean>  
  7.   
  8. <bean id="accountService"  
  9.     class="com.javaforge.rapae.factory.GenericRapaeFactoryBean">  
  10.     <property name="target"  
  11.         value="test.com.rapae.service.AccountService" />  
  12.     <property name="rapaeProxyHandler" ref="rapaeProxyHandler" />  
  13. </bean>  
 <bean id="rapaeProxyHandler"
  class="com.javaforge.rapae.handler.JPARapaeProxyHandler"
  scope="prototype">
  <property name="entityManagerFactory"
   ref="entityManagerFactory" />
 </bean>

 <bean id="accountService"
  class="com.javaforge.rapae.factory.GenericRapaeFactoryBean">
  <property name="target"
   value="test.com.rapae.service.AccountService" />
  <property name="rapaeProxyHandler" ref="rapaeProxyHandler" />
 </bean>

 JPARapaeProxyHandler类是负责为Rapae实现代理生成JPA调用的执行者

GenericRapaeFactoryBean则是一个实现了Spring的FactoryBean接口的一个工厂类

 

然后我们就可以像正常的Spring中被托管的Bean一样使用这个accountService了。

 

如果很不幸,你要求的业务逻辑比较复杂(这种情况比比皆是,业务逻辑本来就是为了处理比较繁琐的事情而存在,DAO封装了数据访问后,就可以在业务层安心的进行数据无关的业务操作)

那么,我们就可以用一个抽象类去实现接口中某些复杂业务的方法。其他的简单的业务逻辑的方法则仍可由Rapae去代理实现。

Java代码 复制代码
  1. import java.util.List;   
  2. import org.springframework.orm.jpa.support.JpaDaoSupport;   
  3. import org.springframework.transaction.annotation.Transactional;   
  4. import test.com.rapae.model.Account;   
  5.   
  6. @Transactional  
  7. public abstract class AccountServiceImpl extends JpaDaoSupport implements AccountService {   
  8.        
  9.     public Account login(String username, String password) {   
  10.         List<?> result = getJpaTemplate().find("from Account where username = ? and password = ?", username, password);   
  11.         return (Account) (result.isEmpty()?null:result.get(0));   
  12.     }   
  13.   
  14. }  
import java.util.List;
import org.springframework.orm.jpa.support.JpaDaoSupport;
import org.springframework.transaction.annotation.Transactional;
import test.com.rapae.model.Account;

@Transactional
public abstract class AccountServiceImpl extends JpaDaoSupport implements AccountService {
 
 public Account login(String username, String password) {
  List<?> result = getJpaTemplate().find("from Account where username = ? and password = ?", username, password);
  return (Account) (result.isEmpty()?null:result.get(0));
 }

}

 

当然,对应的Spring配置也得做出相应的修改

Xml代码 复制代码
  1. <bean id="accountService"  
  2.     class="com.javaforge.rapae.factory.GenericRapaeFactoryBean">  
  3.     <property name="target"  
  4.         value="test.com.rapae.service.AccountServiceImpl" />  
  5.     <property name="rapaeProxyHandler" ref="rapaeProxyHandler" />  
  6. </bean>  
 <bean id="accountService"
  class="com.javaforge.rapae.factory.GenericRapaeFactoryBean">
  <property name="target"
   value="test.com.rapae.service.AccountServiceImpl" />
  <property name="rapaeProxyHandler" ref="rapaeProxyHandler" />
 </bean>

 我们可以看到,target属性由原来的test.com.rapae.service.AccountService改变为test.com.rapae.service.AccountServiceImpl,其他都不用做任何改变。


剩下的事情大家都可以猜到了:按正常由Spring托管的accountService使用。

 

下面给出我对我的构想的一个基于JPA的参考实现,当然只要你实现了相应的接口就可以写出基于Hibernate或者基于其他ORM框架的实现。比较抱歉的是,我个人不打算将其做成XX框架(本来想做成Java On Rails的,看来....-_-!!),也没这个能力和时间。所以觉得,这个能作为一个弱化DAO层的方案提出,让大家在平时中能进行参考是最好的选择。

 

该参考实现只涉及到具体实现代码,不自带编译时需要的包。若想编译,请按列表中的包自行准备。

  1. antlr-2.7.6.jar
  2. asm-3.1.jar
  3. cglib-2.2.jar
  4. commons-collections.jar
  5. commons-logging.jar
  6. dom4j-1.6.1.jar
  7. ejb3-persistence.jar
  8. hibernate3.jar (版本3.2)
  9. hibernate-annotations.jar
  10. hibernate-commons-annotations.jar
  11. hibernate-entitymanager.jar (版本3.3.2.GA)
  12. javassist.jar
  13. jta.jar
  14. junit-4.4.jar
  15. log4j-1.2.15.jar
  16. ojdbc14.jar (这个是ORACLE的JDBC驱动,如果你用其他的数据库例如MySQL,请自行替换成对应的JDBC驱动)
  17. spring.jar (版本2.5.4)

若想跑起测试用例,请自行配置 persistence.xml

 

  • rapae.jar (26.8 KB)
  • 描述: 打包好的rapae
  • 下载次数: 27
  • Rapae-src.zip (85.4 KB)
  • 描述: Rapae的参考实现的源代码

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics