锁定老帖子 主题:Rapae 弱化DAO的一种方法
精华帖 (0) :: 良好帖 (19) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-06-15
最后修改:2010-06-23
可怜的DAO层已经被各位大侠蹂躏得体肤完肤了,从范型DAO一直被蹂躏到现在只剩下一个可怜巴巴的接口,无不体现Java人追求敏捷开发的热情。其实,DAO层本来的作用就应该自从Hibernate一类优秀的ORM框架诞生之日起就应该消失灭迹了的。既然如此,那么我们就毁灭得更彻底一点。
下面是我对Service与DAO层整合的一些构想
约定优先于配置
query属性:查询语句或者命名查询名称
count属性:用于统计个数的查询语句或者命名查询名称,固定返回为一个Long型的数据
PS:当接口的方法没有Rapae注释的时候,按query与count均属于默认值情况处理
注释定义:
详细说明:
注释定义:
查询行为方式:
查询条件传递:
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-06-15
首先我们先来看效果:
假设我们有这样一个Entity类 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; } }
那么,我们在实际的开发过程中,只用写一个接口就能实现大部分的业务逻辑功能。 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中这样配置 <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了。 |
|
返回顶楼 | |
发表时间:2008-06-15
如果很不幸,你要求的业务逻辑比较复杂(这种情况比比皆是,业务逻辑本来就是为了处理比较繁琐的事情而存在,DAO封装了数据访问后,就可以在业务层安心的进行数据无关的业务操作) 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配置也得做出相应的修改 <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,其他都不用做任何改变。
|
|
返回顶楼 | |
发表时间:2008-06-15
下面给出我对我的构想的一个基于JPA的参考实现,当然只要你实现了相应的接口就可以写出基于Hibernate或者基于其他ORM框架的实现。比较抱歉的是,我个人不打算将其做成XX框架(本来想做成Java On Rails的,看来....-_-!!),也没这个能力和时间。所以觉得,这个能作为一个弱化DAO层的方案提出,让大家在平时中能进行参考是最好的选择。
该参考实现只涉及到具体实现代码,不自带编译时需要的包。若想编译,请按列表中的包自行准备。
若想跑起测试用例,请自行配置 persistence.xml
|
|
返回顶楼 | |
发表时间:2008-06-16
http://www.iteye.com/post/488026
跟这个有什么区别? 而且你的代理是用CGLIB实现的, 仅仅是接口的话,完全不如动态代理来的舒服。 |
|
返回顶楼 | |
发表时间:2008-06-16
HOHO,非常抱歉,我真的没注意看Norther也发表过类似的文章。我做毕业设计好久了,几个月都没关注JavaEye。不过确实是小弟独自构想的,只能说我们想得比较接近了,绝无抄袭之意。
对于Norther的疑问: Norther 写道
而且你的代理是用CGLIB实现的, 仅仅是接口的话,完全不如动态代理来的舒服。
我实际上是这样处理的:遇到接口的时候采用JDK Proxy,遇到类的时候用CGLib
public Object getObject() throws Exception { if (getTarget().isInterface()) { // 如果是接口,则用JDK Proxy进行代理 return getInterfaceRapaeProxy().proxy(getTarget(), getRapaeProxyHandler()); } else { // 如果是类,则用CGLib进行代理 Object object = getClassRapaeProxy().proxy(getTarget(), getRapaeProxyHandler()); //自动封装 applicationContext.getAutowireCapableBeanFactory() .autowireBeanProperties(object, AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT, false); return object; }// if }
至于不同点,我大概看了一下Norther的帖子--估计一时半会不能完全领悟您帖子的精髓,所以只能用“大概”这种粗糙的词语--个人提出点不同点:
目前就这些吧,恩恩,比较开心了。终于有人回复了...,而且也证明我的想法并不孤单,呵呵。THX |
|
返回顶楼 | |
发表时间:2008-06-16
DAO又被蹂躏了~~~
这种方式看起来真的非常简洁。 比起泛型DAO来最大的好处无非是改接口而已。不再是继承。 DAO和Service我一直检查合并在一起。所以采用泛型DAO并不会带来问题。 这样简洁的写法让我比较心动,但是仔细想一下,把那些注解放到代码中也只是一行代码的问题并不会带来多大的简化。 |
|
返回顶楼 | |
发表时间:2008-06-16
呵呵,我那个东东不能满足的东西太多了,其就是根据Annotation描述的操作去组装Hibernate API,目的就是简化大部分简单的操作而根本不用写实现类,如果有复杂操作,那就自己再写个类去实现好了,至于你所说的写个抽象类,自己实现复杂的,然后抽象的简单操作用CGLIB去代理,我想这样可能也有他的用武之地,但是意义不大,你都利用Annatation去描述你的操作了,目的就是不想自己再去写一个类实现,现在的有了Spring,Hibernate,大多数dao的代码都非常简单,我自己写个抽象类,那剩下的简单的操作我写两行代码再实现一下也并不麻烦,例如createQuery("from Student s where s.name =?"),这种东西,没必要再用CGLIB代理一下,等于把那个HQL放到Annotation里,工作量区别都不大,因为都很简单,而你那样就不纯粹了,不过这都是小问题。
另外注入方面,没什么好说的,你那个功能确实好,确实应该提供这么一种和Spring结合更紧密的方法。 关于线程安全问题,我这方面比较薄弱,还看不出我那样有什么线程安全问题,我也不理解为什么要在create方法上加synchronized,麻烦请指明。 最后那个isSingleton始终返回false,是我笔误了,应该始终返回true,不好意思了,在程序中,有几个需要实现的接口,就应当有几个相应的代理,不多不少刚刚好,我在AutoInjectDynamicDaoBeanPostProcessor里的daoCache,也是这个意思。 |
|
返回顶楼 | |
发表时间:2008-06-16
fireflyc 写道 DAO又被蹂躏了~~~
这种方式看起来真的非常简洁。 比起泛型DAO来最大的好处无非是改接口而已。不再是继承。 DAO和Service我一直检查合并在一起。所以采用泛型DAO并不会带来问题。 这样简洁的写法让我比较心动,但是仔细想一下,把那些注解放到代码中也只是一行代码的问题并不会带来多大的简化。 我的意思就是该兄弟的最后一句,其实简化的并不大,这个工具真正的意义在于没有实现类,楼主那样再写个实现类出来,就基本丧失了这个工具最大的意义。 |
|
返回顶楼 | |
发表时间:2008-06-16
Norther 写道
关于线程安全问题,我这方面比较薄弱,还看不出我那样有什么线程安全问题,我也不理解为什么要在create方法上加synchronized,麻烦请指明。
其实去学校的路上我又仔细想了想,由于Spring在加载配置的时候实际上是单线程的,并不会出现我所谓的多线程问题,是我自己敏感了。我自己在开始YY的时候并不打算写死在Spring框架中,所以考虑到了多线程的问题...现在回头想想其实挺多余.. 如果要考虑多线程并发,之所以要加synchronized,就是要防止create方法在完成之前再次被其他线程调用,这个时候的状态就不可预知了。可能会被其他线程调用么?可以说是肯定会的,因为你是静态的方法,而所有在不同线程的实例都在企图同时调用他。 fireflyc 写道
这样简洁的写法让我比较心动,但是仔细想一下,把那些注解放到代码中也只是一行代码的问题并不会带来多大的简化。
Norther 写道
我的意思就是该兄弟的最后一句,其实简化的并不大,这个工具真正的意义在于没有实现类,楼主那样再写个实现类出来,就基本丧失了这个工具最大的意义。
厄...貌似被插中软肋了...我原本的构思是这样的:
|
|
返回顶楼 | |