您的位置:首页 > 运维架构 > 网站架构

Java Web Application 自架构 三 通用DAO类实现

2012-12-11 17:35 337 查看
准备好了单元测试模块,就写个底层的数据访问的控制Handle类来试试吧。也就是通常我们所说的DAO类。一般而言,每个模型/实体类需要建立一个DAO类。
不过经验告诉我们,一些通用的CRUD方法几乎在每个DAO类中都是一样的,那么也就是说我们通常所写的DAO会有些代码冗余的问题,而且根据业务需要,日后的维护工作有可能添加一些新的实体类,写地越多,DAO类就越多,写的代码就越多余还浪费时间。如何将写好的一个DAO类进行代码重用成为了问题。其实笔者见过一个的系统里有做过将Hibernate框架人应用做成静态类,用到时直接HibernateUtil.save()的。今天,咱也做一个通用的DAO放一些通用的CRUD方法进去,用来做通用的CRUD操作。这样做既省去了不少代码量,加强了代码可读性,而且还方便快捷地架构起这个WebModel,可以重用到很多的系统中。当然,遇到特殊的Entity
需要建个别的DAO时再当别论。
首先是接口的编写,我在接口的编写时加了很多的JavaDoc,就不贴出代码了,上传为附件方便读者下载吧。此处列出几个方法做简单说明
package com.xxxxx.webmodel.pin;

import java.util.List;
import java.util.Map;
public interface IPersistencePin {
public void createPersistingEntity(Object persistingObj) throws Exception;
public <PerE> PerE retrievePersistedEntity(Class<PerE> persistedClass,
long id) throws Exception;
public <PerE> PerE retrievePersistedEntity(Class<PerE> persistedClass,
String identifier) throws Exception;
public <PerE> List<PerE> retrievePersistedEntityAllInList(
Class<PerE> persistedClass) throws Exception;
public void updatePersistedEntity(Object persistingObj) throws Exception;
public void deletePersistedEntity(Object persistingObj) throws Exception;
}


接口的实现:
package com.xxxxx.webmodel.pin.impl;

/*省略多个import 语句*/
import com.gxino.webmodel.pin.IPersistencePin;
@Repository
public class HibernatePersistencePin implements Serializable,IPersistencePin {
private static final long serialVersionUID = -295800418896667101L;
private SessionFactory sessionFactory;
public static long getSerialversionuid() {
return serialVersionUID;
}

public SessionFactory getSessionFactory() {
return sessionFactory;
}
@Resource
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}

其中,第一个方法
createPersistingEntity(Object persistingObj)
将Map到Hibernate
中的任意Pojo的对象持久化,即将某实体类的一个对象的各项属性值存入数据库。
@Override
public void createPersistingEntity(Object persistingObj) throws Exception {
Session session = null;
boolean needCloseSession = false;
try{
session = sessionFactory.getCurrentSession();
if(session==null)throw new HibernateException("No current Session");
}catch(HibernateException he){
session = sessionFactory.openSession();
needCloseSession = true;
}
try{
session.save(persistingObj);
if(needCloseSession)session.close();
}catch(HibernateException he){
throw new Exception("Save Pojo failed, because of "+he.getMessage());
}
}

笔者在该方法中做了currentSession与openSession双处理,也就是说该方法既可以是业务逻辑进行事务中的某一句,也可以无事务信赖地被单独调用。后面的update
方法与delete方法以及通过id从数据库中查出该对象的方法是如出一辙,只是session.save(persistingOjb)换成其他语句,就不贴代码了。贴一下另两个方法的第二个Try块中的代码吧:

1<PerE>
PerE retrievePersistedEntity(Class<PerE> persistedClass,
String uniquePropertyName, Object propertyValue)
传入一个唯一键值的名值对找出类对象:
Criteria criteria = session.createCriteria(persistedClass);
criteria.add(Restrictions.eq(uniquePropertyName, propertyValue));
@SuppressWarnings("unchecked")
PerE result = (PerE)criteria.uniqueResult();
if(needCloseSession)session.close();
return result;


这里用到了Hibernate的Criteria与Restrictions,其实还有个Example类。
关于这一点,网络上有好多高手有解盲它的专题用法的帖子,笔者就不作大篇文章说它了。感觉这个比起写hql更 『面向对象』 一点儿,好用且易理解,所以特意贴在这里一个,以提醒自己与读者们可以这样去写。其实用了它的代码都是白话文,简单提一下: 从Session中创建一个某pojo人“基准”,用一些 “条件”,或是举例的方式,限定这个查询基准,之后按这个限定的标准查出具体对象。
查找所有对象到一个List中也不多说了,criteria不add任何限制,直接criteria.list();就好.

2<PerE> PerE retrievePersistedEntity(Class<PerE>
persistedClass,
String identifier)
throws Exception;吧
无需给出唯一键的名,只需要唯一键的值,即去查询到该实体类对象。不过由于一个表中,可能有多个唯一键,这样就会产生冲突。例如,用户名和Email两个唯一性字段,用户A用aaa@aaa.com作用户名,用户B用aaa@aaa.com作email,这种情况下查询到用户的结果就不仅是A而且还有B了。所以笔者将它在接口里就标了@deprecated
同样,只贴第二个try块中应放的代码。
String propertyName=null;
Method[] methodArray =persistedClass.getMethods();
if(methodArray!=null)for(Method method:methodArray){
if(method.getName().startsWith("get")){
Criteria criteria = session.createCriteria(persistedClass);
Annotation anno = method.getAnnotation(Column.class);
if(anno.toString().contains("unique=true")){
propertyName = Character.toString(method.getName().charAt(3)).toLowerCase()+method.getName().substring(4);
criteria.add(Restrictions.eq(propertyName, identifier));
}
@SuppressWarnings("unchecked")
PerE result = (PerE)criteria.uniqueResult();
if(needCloseSession)session.close();
return result;
}
}
if(needCloseSession)session.close();
return null;


这方法的实现需要javax.persistence.Column这一注解到实体类中的每一个属性做配合。换句话说,Hibernate的Mapping是注解方式的。
Column注解在JPA的规范中, hibernate框架有JPA的实现,在hibernate-jpa-2.0-api-x.x.x-Final.jar包中。 上面代码用到了反射,主要意思是:找到该Pojo类中所有属性的getter,通过查看该getter上是否注解有@Column并且注解的一个属性unique值为true,来找到相应的属性名,然后用名值对限定查询标准去查出实体类对象。
至于怎么用JPA注解配置Pojo,也可以作为专题发表一篇文章了,笔者就不多叙述,直接在附件夹带一些实体类了,实体类如何map到sessionFactory中也在第一篇《xxx
注解化配置》的ApplicationContext.java中出现,代码在配置sessionFactory
的@Bean的方法中:
@SuppressWarnings("rawtypes")
Class[] entities = new Class[2];
entities[0] = AccountEntity.class;
entities[1] = ActionLogEntity.class;
asfBean.setAnnotatedClasses(entities);


另外,有些读者可能已经发现,上述通用DAO中的实现中,每个方法也几乎重用了好多地方,只是改变了中间的核心部分,可不可以用一个固定的方法将每个方法的核心语句包住,就像是让Spring代管Transactional一样,很明显,我们会想到AOP,顺便将log也做到AOP中,不过这一篇就不多讨论了。先就这样用着。
最后,别忘了写单元测试类。
package com.xxxxx.webmodel.test.pin;

import static org.junit.Assert.fail;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;

import com.xxxxx.webmodel.entity.AccountEntity;
import com.xxxxx.webmodel.entity.ActionLogEntity;
import com.xxxxx.webmodel.pin.IPersistencePin;
import com.xxxxx.webmodel.util.ApplicationContext;
import com.xxxxx.webmodel.util.WebConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={ApplicationContext.class})
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class })
@TransactionConfiguration(defaultRollback=true,transactionManager="hibernateTransactionManager")
@Transactional
public class PersistencePinTest {
private IPersistencePin persistPin;
private Map<String,Object> usedStuff;

public IPersistencePin getPersistPin() {
return persistPin;
}
@Resource
public void setPersistPin(IPersistencePin persistPin) {
this.persistPin = persistPin;
}

@BeforeClass
public static void init() throws Exception{
new WebConfiguration().onStartup(null);
}

@Before
public void setUp() throws Exception{
if(usedStuff==null)
usedStuff = new HashMap<String,Object>();
AccountEntity account = new AccountEntity();
account.setUserName("junittest");
account.setPassword("junittest");
account.setEmail("junittest@junit.com");
account.setAlias("unitTest");
account.setAge((short)18);
account.setGender("Male");
account.setCreatedAt(new Date());
persistPin.createPersistingEntity(account);
usedStuff.put("userId", new Long(account.getId()));
usedStuff.put("userName", account.getUserName());
usedStuff.put("userEmail", account.getEmail());

ActionLogEntity actionLog = new ActionLogEntity();
actionLog.setAction("Junit Test");
actionLog.setActor(account);
actionLog.setHappenTime(new Date());
try{persistPin.createPersistingEntity(actionLog);}
catch(Exception e){
e.printStackTrace();
}
usedStuff.put("actionId", new Long(actionLog.getId()));
usedStuff.put("actionTime", actionLog.getHappenTime());
}

@Test
@Ignore
public final void testHibernateSession() throws Exception{
/*Please not execute @Before setUp() method, for test this, and NO              @TransactionConfiguration NO @Transactional*/
/*Also make other test methods @Ignore*/
AccountEntity account = new AccountEntity();
account.setUserName("junittest");
account.setPassword("junittest");
account.setEmail("junittest@junit.com");
account.setAlias("unitTest");
account.setAge((short)18);
account.setGender("Male");
account.setCreatedAt(new Date());
persistPin.createPersistingEntity(account);
persistPin.deletePersistedEntity(account);
}
}

具体的其他测试方法在附件的文件中,或是读者们自行去写会比笔者的要好很多。

附件中有源码,在很下面哦,请您仔细找一下.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: