论坛首页 Java企业应用论坛

框架底层综合+快速开发+代码重用框架-设计(Dao层)

浏览 4850 次
该帖已经被评为隐藏帖
作者 正文
   发表时间:2010-09-20  

 

二、Dao

Dao层是数据化持久层,以前我们习惯使用JDBC做链接,现在我们使用JPA,结合Spring的优势,使用连接池来配置、以及与数据库的同步!我们使用JAP+Spring,只需要建立数据库即可以,使用所谓的“逆向工程”——老程序员们都这样称呼它,我觉得这样才是真正的面向对象的设计。

1、基础抽象接口DAO,以后让所对象的Dao都来实现这个借口。

对于一个dao层来说,是处理Service传递进来的数据与数据库同步的,所以一般的方法就是CRUD的方法,对于每一种对象来说,都是需要进行增删改查,然而在Model层中,不同的对象经过JPA的映射之后会生成不同的表,所以我们dao层就需要针对不同的对象都有增删改查,所以使用反射式比较适合的选择(这个抽象类中包含了通用的CRUD的方法)。

该类是其他所有Dao的接口,应该具备以下几个通用的操作:

 

1.//增加一个实体对象	
public abstract void add(T paramT);
	2.//删除一个实体
public abstract void delete(Integer paramInteger);
	3.//修改一个对象
public abstract void modify(T paramT);
	4.//查询单个对象(通过Model.id)
public abstract T query(Integer paramInteger);
	5.//分页查询
public abstract ModelSet<T> queryAll(int firstindex, int maxresult, String wherejpql, Object[] queryParams, LinkedHashMap<String, String> orderby);
	6.//Compass全文检索
public abstract List<T> search(String paramString);
	7.//反射获取Class
public abstract Class<T> getModelClass();

 

 //参见如下的代码。

package com.jxs.sys.core.base.dao;

import java.util.LinkedHashMap;
import java.util.List;

import com.jxs.sys.core.base.model.Model;
import com.jxs.sys.core.base.model.ModelSet;
/**
 * 
 * @目的 数据持久层接口	
 * @时间 Apr 24, 2010
 * @作者 BestUpon
 * @邮箱 bestupon@foxmail.com
 *
 * @param <T>
 */

public abstract interface Dao<T extends Model> {
	public abstract void add(T paramT);

	public abstract void delete(Integer paramInteger);

	public abstract void modify(T paramT);

	public abstract T query(Integer paramInteger);

	public abstract ModelSet<T> queryAll(int firstindex, int maxresult, String wherejpql, Object[] queryParams, LinkedHashMap<String, String> orderby);

	public abstract List<T> search(String paramString);

	public abstract Class<T> getModelClass();
}

2、写一个真正的处理数据的Dao层类-DaoOperation

DaoOperation 这个类是真正的处理和数据库同步的类,由于们使用的JPAHibernate实现,所以我们要构建一个实体管理器,EnteryManager

例如:

 

//持久化上下文	
@PersistenceContext
protected EntityManager em;

 使用这个实体管理器去操作所有的实体对象。

 

我们针对public abstract interface Dao<T extends Model>{}这个类中的所有方法,要在DaoOperation 这个类中去真正的操作数据,所以我们要对外公开一下几个方法:

 

1.//删除实体对象
public <T> void delete(Class<T> entityClass, Object entityid);
2.//查询试题对象
public <T> T find(Class<T> entityClass, Object entityId);
3.//持久化实体对象
public void save(Object object);
4.//统计实体对象个数
public <T> int getCount(Class<T> entityClass);
5.//修改更新实体对象
public void update(Object object)
6.//分页+排序查询
public <T> ModelSet<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult, LinkedHashMap<String, String> orderby);
7.//分页+JPQL 查询
public <T> ModelSet<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult, String wherejpql, Object[] queryParams);
8.//单纯分页整体查询
public <T> ModelSet<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult);
9.//无分页,整体查询
public <T> ModelSet<T> getScrollData(Class<T> entityClass);
10.//分页+JPQL+排序查询
public <T> ModelSet<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult, String wherejpql, Object[] queryParams, LinkedHashMap<String, String> orderby);
这样一个底层持久化操作类,实现了所有的同步操作。
11.//清缓存方法
clear();

 

 还有两个属性:

 

//ehCache缓存管理器
	@Resource(name = "ehCache")
	private Cache cache;
	//Compass搜索索引管理器
	@Resource(name = "indexManager")
	private IndexManager indexManager;

 说明:上面有有个ModelSet,这个对象是是查询到的数据的集合,大家想想应该这么设计呢?

 

我是这样设计的:

 

1.//将查询到的结果转换成一个这个对象的List集合,方便前台,操作(例如Struts2标签中的操作,DisplayTag显示数据的需要数据等)
 private List<T> resultlist = new ArrayList<T>();
2.//返回一个总体记录数,分页等需要
private long totalrecord = 0L;
3.//查询判断一些不需要的对象在里面,或者需要的对象在里面,这样过滤一些对象的时候需要
private List<String> includeids = new ArrayList<String>();

 上面的方法都给出了,接下来就需要对其实现了,怎么个实现法呢?——很简单,就是拼JPQL查询字符串。

实现代码,参见如下:

 

package com.jxs.sys.core.base.dao;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;

import javax.annotation.Resource;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import net.sf.ehcache.Cache;

import org.hibernate.ejb.QueryImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.jxs.sys.core.base.model.ModelSet;
import com.jxs.sys.core.global.cache.ClearCache;
import com.jxs.sys.core.global.config.utils.ConfigHolder;
import com.jxs.sys.core.search.compass.IndexManager;
/**
 * 
 *@TO 数据同步基础类
 *@des  
 *@author BestUpon
 *@date Sep 20, 2010 4:31:20 PM
 *@version since 1.0
 */
@Transactional
public abstract class DaoOperation {
	protected final Logger log = LoggerFactory.getLogger(super.getClass());

	@PersistenceContext
	protected EntityManager em;

	@Resource(name = "ehCache")
	private Cache cache;

	@Resource(name = "indexManager")
	private IndexManager indexManager;

	public void clear() {
		this.em.clear();
	}

	private <T> void del(Class<T> entityClass, Object entityid) {
		this.em.remove(this.em.getReference(entityClass, entityid));
	}

	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public <T> void delete(Class<T> entityClass, Object entityid) {
		del(entityClass, entityid);

		if (isIndexable()) {
			this.indexManager.deleteIndex(entityClass, entityid);
		}
		if (isOperationSearchResult()) {
			this.log.info("删除对象,删除搜索缓存");
			ClearCache.clear(this.cache, "search" + entityClass.getName());
		}
	}

	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public <T> T find(Class<T> entityClass, Object entityId) {
		return this.em.find(entityClass, entityId);
	}

	public void save(Object object) {
		this.em.persist(object);
		if (isIndexable())
			this.indexManager.createIndex(object);
	}

	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public <T> int getCount(Class<T> entityClass) {
		javax.persistence.Query query = this.em.createQuery("select count(" + getCountField(entityClass) + ") from " + getEntityName(entityClass) + " o");

		setQueryCache(query);

		return ((Integer) query.getSingleResult()).intValue();
	}

	public void update(Object object) {
		this.em.merge(object);

		if (isOperationSearchResult()) {
			this.log.info("更新对象,删除搜索缓存");
			ClearCache.clear(this.cache, "search" + object.getClass().getName());
		}
		if (isIndexable())
			this.indexManager.updateIndex(object);
	}

	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public <T> ModelSet<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult, LinkedHashMap<String, String> orderby) {
		return getScrollData(entityClass, firstindex, maxresult, null, null, orderby);
	}

	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public <T> ModelSet<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult, String wherejpql, Object[] queryParams) {
		return getScrollData(entityClass, firstindex, maxresult, wherejpql, queryParams, null);
	}

	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public <T> ModelSet<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult) {
		return getScrollData(entityClass, firstindex, maxresult, null, null, null);
	}

	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public <T> ModelSet<T> getScrollData(Class<T> entityClass) {
		return getScrollData(entityClass, -1, -1);
	}

	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public <T> ModelSet<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult, String wherejpql, Object[] queryParams, LinkedHashMap<String, String> orderby) {
		return queryData(entityClass, firstindex, maxresult, wherejpql, queryParams, orderby);
	}

	private void setQueryCache(javax.persistence.Query query) {
		if ((!(isCacheable())) || (!(query instanceof QueryImpl)))
			return;
		((QueryImpl) query).getHibernateQuery().setCacheable(true);
	}

	private <T> ModelSet<T> queryData(Class<T> entityClass, int firstindex, int maxresult, String wherejpql, Object[] queryParams, LinkedHashMap<String, String> orderby) {
		ModelSet<T> qr = new ModelSet<T>();
		String entityname = getEntityName(entityClass);
		javax.persistence.Query query = this.em.createQuery("select o from " + entityname + " o " + ((wherejpql == null) ? "" : new StringBuilder("where ").append(wherejpql).toString()) + buildOrderby(orderby));
		setQueryParams(query, queryParams);
		if ((firstindex != -1) && (maxresult != -1)) {
			query.setFirstResult(firstindex).setMaxResults(maxresult);
		}

		setQueryCache(query);
		qr.setResultlist(query.getResultList());
		query = this.em.createQuery("select count(" + getCountField(entityClass) + ") from " + entityname + " o " + ((wherejpql == null) ? "" : new StringBuilder("where ").append(wherejpql).toString()));
		setQueryParams(query, queryParams);

		setQueryCache(query);
		qr.setTotalrecord(((Long) query.getSingleResult()).longValue());
		return qr;
	}

	protected void setQueryParams(javax.persistence.Query query, Object[] queryParams) {
		if ((queryParams != null) && (queryParams.length > 0))
			for (int i = 0; i < queryParams.length; ++i)
				query.setParameter(i + 1, queryParams[i]);
	}

	protected String buildOrderby(LinkedHashMap<String, String> orderby) {
		StringBuffer orderbyql = new StringBuffer("");
		if ((orderby != null) && (orderby.size() > 0)) {
			orderbyql.append(" order by ");
			for (String key : orderby.keySet()) {
				orderbyql.append("o.").append(key).append(" ").append((String) orderby.get(key)).append(",");
			}
			orderbyql.deleteCharAt(orderbyql.length() - 1);
		}
		return orderbyql.toString();
	}

	protected <T> String getEntityName(Class<T> entityClass) {
		String entityname = entityClass.getSimpleName();
		Entity entity = (Entity) entityClass.getAnnotation(Entity.class);
		if ((entity.name() != null) && (!("".equals(entity.name())))) {
			entityname = entity.name();
		}
		return entityname;
	}

	protected <T> String getCountField(Class<T> clazz) {
		String out = "o";
		try {
			PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
			for (PropertyDescriptor propertydesc : propertyDescriptors) {
				Method method = propertydesc.getReadMethod();
				if ((method != null) && (method.isAnnotationPresent(EmbeddedId.class))) {
					PropertyDescriptor[] ps = Introspector.getBeanInfo(propertydesc.getPropertyType()).getPropertyDescriptors();
					out = "o." + propertydesc.getName() + "." + ((!(ps[1].getName().equals("class"))) ? ps[1].getName() : ps[0].getName());
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return out;
	}

	public static boolean isCacheable() {
		return ConfigHolder.getConfig().isUseCache();
	}

	public static boolean isIndexable() {
		return ConfigHolder.getConfig().isUseIndex();
	}

	public static boolean isOperationSearchResult() {
		return ConfigHolder.getConfig().isOperationSearchResult();
	}
}

 大家也学觉得很莫名其妙,为什么只给出两个抽象类呢?接下来我们将这两个类“串起来”,看看会有什么效果呢?我先给出类来,大家看看,要怎么实现呢?public abstract class DaoSupport<T extends Model> extends DaoOperation implements Dao<T> {}

 

 也学大家会很奇怪,怎么又一个抽象类呢?能不能弄点新鲜玩意,就会写抽象类呢?嘿嘿。。。慢慢向下看。

看到了没有,上面有个:implements Dao<T>,这就意味着,在这个类中就Dao 这几个方法,如果让你实现反射的话,或许有更好的方法,个人是这样实现的,利用一下构造方法。

public DaoSupport() {
		this.modelClass = ReflectionUtils.getSuperClassGenricType(super.getClass());
	}
 这样做的有什么好处呢?巧妙的运用的Spring的注入的时候,利用空构造函数实例化一个对象,巧借给这个对象的modelClass 赋值。
这里主要是讲解一种思路,具体的反射,如有需要,以后在讨论。
由于前面的DaoOperation这个类已经要将做的所有事情都做完了,这里直接使用super.xxx(xxx);这样的方式很轻松的就做好了。参见如下代码:

 package com.jxs.sys.core.base.dao;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;

import javax.annotation.Resource;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.lang.StringUtils;
import org.compass.core.Compass;
import org.compass.core.CompassHits;
import org.compass.core.CompassSession;
import org.compass.core.CompassTemplate;
import org.perf4j.aop.Profiled;

import com.jxs.sys.core.base.event.AddEvent;
import com.jxs.sys.core.base.event.ModifyEvent;
import com.jxs.sys.core.base.listeners.AuditListener;
import com.jxs.sys.core.base.model.Model;
import com.jxs.sys.core.base.model.ModelSet;
import com.jxs.sys.core.base.utils.ReflectionUtils;
import com.jxs.sys.core.base.utils.WebUtil;
import com.jxs.sys.core.global.cache.ClearCache;
import com.jxs.sys.core.security.model.User;
import com.jxs.sys.core.security.userholder.UserHolder;

/**
 * 
 * @TO 数据操作支持类
 * @des
 * @author BestUpon
 * @date Sep 20, 2010 4:33:28 PM
 * @version since 1.0
 * @param <T>
 */
public abstract class DaoSupport<T extends Model> extends DaoOperation implements Dao<T> {

	@Resource(name = "compassTemplate")
	private CompassTemplate compassTemplate;

	@Resource(name = "ehCache")
	private Cache cache;
	/**
	 * 这个属性是权限管理用的,这里不需要管他
	 */
	@Resource(name = "auditListener")
	private AuditListener auditListener;
	private Class<T> modelClass;

	@Profiled(tag = "DaoSupport")
	public DaoSupport() {
		this.modelClass = ReflectionUtils.getSuperClassGenricType(super.getClass());
	}

	public Class<T> getModelClass() {
		return this.modelClass;
	}

	@Profiled(tag = "addInDao", message = "model = {$0}")
	public void add(T model) {
		AddEvent addEvent = new AddEvent(model);
		this.auditListener.onEvent(addEvent, this);

		super.save(model);
	}

	@Profiled(tag = "deleteInDao", message = "modelId = {$0}")
	public void delete(Integer modelId) {
		super.delete(getModelClass(), modelId);
	}

	@Profiled(tag = "modifyInDao", message = "model = {$0}")
	public void modify(T model) {
		ModifyEvent modifyEvent = new ModifyEvent(model);
		this.auditListener.onEvent(modifyEvent, this);

		super.update(model);
	}

	@Profiled(tag = "queryInDao", message = "modelId = {$0}")
	public T query(Integer modelId) {
		return (T) ((Model) super.find(getModelClass(), modelId));
	}

	@Profiled(tag = "queryAllInDao", message = "firstindex = {$0}, maxresult = {$1}, wherejpql = {$1}, queryParams = {$1}, orderby = {$1}")
	public ModelSet<T> queryAll(int firstindex, int maxresult, String wherejpql, Object[] queryParams, LinkedHashMap<String, String> orderby) {
		return super.getScrollData(getModelClass(), firstindex, maxresult, wherejpql, queryParams, orderby);
	}

	@Profiled(tag = "searchInDao", message = "queryString = {$0}")
	public List<T> search(String queryString) {
		User user = UserHolder.getCurrentLoginUser();
		ModelSet<T> qr = null;
		Element element = null;

		Compass compass = this.compassTemplate.getCompass();

		CompassSession session = compass.openSession();
		List models = null;

		CompassHits hits = session.find(queryString);
		this.log.info("命中:" + hits.getLength());
		this.log.info("查询字符串:" + queryString);

		models = hightlight(hits);

		session.close();

		Comparator comparter = new BeanComparator("id");
		Collections.sort(models, comparter);
		Collections.reverse(models);

		qr = new ModelSet();
		qr.setResultlist(models);
		qr.setTotalrecord(models.size());

		ClearCache.clear(this.cache, user.getCacheName() + "search" + getModelClass().getName());

		element = new Element(user.getCacheName() + "search" + getModelClass().getName(), qr);
		this.cache.put(element);

		return models;
	}

	@Profiled(tag = "hightlightInDao", message = "hits = {$0}")
	private List<T> hightlight(CompassHits hits) {
		List models = new ArrayList();
		for (int i = 0; i < hits.length(); ++i) {
			Model model = null;
			if (hits.data(i).getClass() == getModelClass()) {
				model = (Model) hits.data(i);

				for (String searchProperty : model.getSearchProperties()) {
					try {
						String[] multiLevelProperties = searchProperty.split(":");
						String value = hits.highlighter(i).fragment(multiLevelProperties[0]);
						if (!(StringUtils.isEmpty(value))) {
							String[] searchExpressions = multiLevelProperties[0].split("_");
							String realProperty = searchExpressions[(searchExpressions.length - 1)];
							String[] properties = (String[]) null;
							if (multiLevelProperties.length > 1) {
								properties = multiLevelProperties[1].split("_");
							}
							List<Model> objs = new ArrayList<Model>();
							objs.add(model);

							if (properties != null) {
								for (String props : properties) {
									List now = new ArrayList();
									for (Model temp : objs) {
										if (props.endsWith("$")) {
											List temps = (List) ReflectionUtils.getFieldValue(temp, props.substring(0, props.length() - 1));
											now.addAll(temps);
										} else {
											Model next = (Model) ReflectionUtils.getFieldValue(temp, props);
											now.add(next);
										}
									}
									objs = now;
								}
							}

							for (Model obj : objs)
								try {
									Object o = ReflectionUtils.getFieldValue(obj, realProperty);
									if (o != null) {
										String oldValue = o.toString();
										String newValue = WebUtil.removeHightlight(value);
										if ((StringUtils.isNotEmpty(oldValue)) && (StringUtils.isNotEmpty(newValue)) && (newValue.equals(oldValue)))
											ReflectionUtils.setFieldValue(obj, realProperty, value);
									}
								} catch (Exception e) {
									this.log.info("添加高亮,给对象【" + obj.getMetaData() + "】设置属性【" + realProperty + "】失败,值为:【" + value + "】");
								}
						}
					} catch (Exception e) {
						this.log.info("处理" + searchProperty + "高亮抛出异常,忽略,原因:" + e.getCause());
					}
				}

				models.add(model);
			} else {
				this.log.info("搜索出不是" + getModelClass() + "的实例" + hits.data(i));
			}
		}

		return models;
	}
}

 那么其他的对象的Dao需要怎么做呢?

UserDao
package com.jxs.sys.core.security.dao;

import com.jxs.sys.core.base.dao.Dao;
import com.jxs.sys.core.security.model.User;

public abstract interface UserDao extends Dao<User> {
	public abstract User queryUserByName(String paramString);
}
那么他的实现呢?
package com.jxs.sys.core.security.dao.jpa;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.jxs.sys.core.base.dao.DaoSupport;
import com.jxs.sys.core.security.dao.UserDao;
import com.jxs.sys.core.security.model.User;

@Repository("userDao")
public class UserDaoJpa extends DaoSupport<User> implements UserDao {
	public User queryUserByName(String userName) {
		String wherejpql = " o.username=?1";
		String[] par = new String[]{userName};
		this.log.info("查找username为 " + userName + " 的用户");
		List<User> users = super.getScrollData(User.class, -1, -1, wherejpql, par).getResultlist();

		if ((users == null) || (users.size() != 1)) {
			this.log.info("不存在用户名为 【" + userName + "】的用户");
			return null;
		}
		return ((User) users.get(0));
	}
}
 

是不是很简单呢?几乎 不需要你写什么代码,所有的CURD的方法都有了!
为什么要举这个例子呢?
1、前面的所有例子都是使用CRUD的简单例子,现在多了一个业务逻辑——按UserName查询?怎么做呢?
很简单,看见没有,UserDao. queryUserByName(String username);就是例子,在UserDaoJpa. queryUserByName(String username);将其实现了,就好了。一般的业务逻辑都是查询,增加一般都是逐条记录的增加。这样是不是很省事呢?
期待:Service层整理中。
 

 

 

 

 

   发表时间:2010-09-20  
HI,看了model和dao层。觉得从思想上面并没有太大的突破。跟springside的差不多。还是泛型dao的思路
0 请登录后投票
   发表时间:2010-09-20   最后修改:2010-09-20
nothink 写道
HI,看了model和dao层。觉得从思想上面并没有太大的突破。跟springside的差不多。还是泛型dao的思路


SpringSide没有使用过!不了解!

由于“s H i t”是JE的敏感词汇,不让提交,所以C o m p a s s H i s就是敏感词汇了,所以在这里不同其他层的连接:

框架底层综合+快速开发+代码重用框架-设计(Model层)

框架底层综合+快速开发+代码重用框架-设计(Dao层)

框架底层综合+快速开发+代码重用框架-设计(Service层)

框架底层综合+快速开发+代码重用框架-设计(Action层)

0 请登录后投票
   发表时间:2010-09-20  
dao层和service层设计差不多,不过使用的ibatis
0 请登录后投票
   发表时间:2010-09-20  
看看巴巴运动网视频一切都那么的easy。
0 请登录后投票
   发表时间:2010-09-21  
差不多,平时都是这样设计的,没新意。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics