二、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 这个类是真正的处理和数据库同步的类,由于们使用的JPA的Hibernate实现,所以我们要构建一个实体管理器,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层整理中。