准备好了单元测试模块,就写个底层的数据访问的控制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.SpringJUnit4Cla***unner;
- 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(SpringJUnit4Cla***unner.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);
- }
- }
具体的其他测试方法在附件的文件中,或是读者们自行去写会比笔者的要好很多。
附件中有源码,在很下面哦,请您仔细找一下