`
掌心童话
  • 浏览: 14753 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

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

阅读更多

        准备好了单元测试模块,就写个底层的数据访问的控制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) MapHibernate 中的任意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());
	}
}
 

      笔者在该方法中做了currentSessionopenSession双处理,也就是说该方法既可以是业务逻辑进行事务中的某一句,也可以无事务信赖地被单独调用。后面的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;
 

 

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

查找所有对象到一个List中也不多说了,criteriaadd任何限制,直接criteria.list();就好.

 

       2<PerE> PerE retrievePersistedEntity(Class<PerE> persistedClass,

String identifier) throws Exception;

        无需给出唯一键的名,只需要唯一键的值,即去查询到该实体类对象。不过由于一个表中,可能有多个唯一键,这样就会产生冲突。例如,用户名和Email两个唯一性字段,用户Aaaa@aaa.com作用户名,用户Baaa@aaa.comemail,这种情况下查询到用户的结果就不仅是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这一注解到实体类中的每一个属性做配合。换句话说,HibernateMapping是注解方式的。 Column注解在JPA的规范中, hibernate框架有JPA的实现,在hibernate-jpa-2.0-api-x.x.x-Final.jar包中。 上面代码用到了反射,主要意思是:找到该Pojo类中所有属性的getter,通过查看该getter上是否注解有@Column并且注解的一个属性unique值为true,来找到相应的属性名,然后用名值对限定查询标准去查出实体类对象。

 

        至于怎么用JPA注解配置Pojo,也可以作为专题发表一篇文章了,笔者就不多叙述,直接在附件夹带一些实体类了,实体类如何mapsessionFactory中也在第一篇《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);
	}
}
 

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

附件中有源码,在很下面哦,请您仔细找一下

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics