[size=large]有分页功能的 HibernateDao
package org.mysterylab.utopiaframework.core.orm.hibernate3;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.mysterylab.utopiaframework.core.orm.Page;
import org.mysterylab.utopiaframework.core.orm.PropertyFilter;
import org.mysterylab.utopiaframework.core.orm.PropertyFilter.MatchType;
import org.mysterylab.utopiaframework.util.reflect.ReflectionUtil;
import org.springframework.util.Assert;
/**
* 在 {@link BaseHibernateDao} 的基础上加入了如下特性:
* <ul>
* <li>1) 分页查询.</li>
* <li>2) 按属性过滤条件列表查询.</li>
* </ul>
*
* @param <T>
* DAO 操作的对象类型
* @param <PK>
* 主键类型
*
* @author
* @see PropertyFilter
*/
public class HibernateDao<T, PK extends Serializable> extends BaseHibernateDao<T, PK> {
/**
* 通过子类的泛型定义获得对象的类型.<br>
* 比如:
* <pre>
* {@code
* public class UserDao extends HibernateDao<User, Long>
* }
* </pre>
*/
public HibernateDao() {
super();
}
/**
* 跳过 DAO 层, 直接在 Service 层使用 BaseHibernateDao 的构造函数,
* 在构造函数中定义对象类型.
* <pre>
* {@code
* HibernateDao<User, Long> userDao = new HibernateDao<User, Long>(sessionFactory, User.class);
* }
* </pre>
* @param sessionFactory
* @param entityClass
*/
public HibernateDao(final SessionFactory sessionFactory,
final Class<T> entityClass) {
super(sessionFactory, entityClass);
}
/**
* 分页获取全部对象.
*
* @param page
* @return
*/
public Page<T> getAll(final Page<T> page) {
return findPage(page);
}
/**
* 按 HQL 分页查询.
*
* @param page
* 分页参数. 注意不支持其中的 orderBy 参数
* @param hql
* hql 语句
* @param values
* 数量可变的查询参数,按顺序绑定
*
* @return 分页查询结果, 附带结果列表及所有查询输入参数
*/
@SuppressWarnings("unchecked")
public Page<T> findPage(final Page<T> page, final String hql,
final Object... values) {
Assert.notNull(page, "page can not be null");
Query query = createQuery(hql, values);
if (page.isAutoCount()) {
long totalCount = countHqlResult(hql, values);
page.setTotalCount(totalCount);
}
setPageParameterToQuery(query, page);
List result = query.list();
page.setResult(result);
return page;
}
/**
* 按 HQL 分页查询.
*
* @param page
* 分页参数. 注意不支持其中的 orderBy 参数
* @param hql
* hql 语句
* @param values
* 命名参数, 按名称绑定
*
* @return 分页查询结果, 附带结果列表及所有查询输入参数
*/
@SuppressWarnings("unchecked")
public Page<T> findPage(final Page<T> page, final String hql,
final Map<String, ?> values) {
Assert.notNull(page, "page can not be null");
Query query = createQuery(hql, values);
if (page.isAutoCount()) {
long totalCount = countHqlResult(hql, values);
page.setTotalCount(totalCount);
}
setPageParameterToQuery(query, page);
List result = query.list();
page.setResult(result);
return page;
}
/**
* 按 Criteria 分页查询.
*
* @param page
* 分页参数.
* @param criterions
* 数量可变的 Criterion
*
* @return 分页查询结果.附带结果列表及所有查询输入参数.
*/
@SuppressWarnings("unchecked")
public Page<T> findPage(final Page<T> page, final Criterion... criterions) {
Assert.notNull(page, "page can not be null");
Criteria criteria = createCriteria(criterions);
if (page.isAutoCount()) {
long totalCount = countCriteriaResult(criteria);
page.setTotalCount(totalCount);
}
setPageParameterToCriteria(criteria, page);
List result = criteria.list();
page.setResult(result);
return page;
}
/**
* 设置分页参数到 Query 对象, 辅助函数.
*
* @param query
* @param page
* @return
*/
protected Query setPageParameterToQuery(final Query query, final Page<T> page) {
Assert.isTrue(page.getPageSize() > 0, "Page#pageSize must larger than 0");
// 注意 Hibernate 的 firstResult 的序号从 0 开始
query.setFirstResult(page.getFirst() - 1);
query.setMaxResults(page.getPageSize());
return query;
}
/**
* 设置分页参数到 Criteria 对象, 辅助函数.
*
* @param criteria
* @param page
* @return
*/
protected Criteria setPageParameterToCriteria(final Criteria criteria,
final Page<T> page) {
Assert.isTrue(page.getPageSize() > 0, "Page Size must larger than zero");
// 注意 Hibernate 的 firstResult 的序号从 0 开始
criteria.setFirstResult(page.getFirst() - 1);
criteria.setMaxResults(page.getPageSize());
if (page.isOrderBySetted()) {
String[] orderByArray = StringUtils.split(page.getOrderBy(), ',');
String[] orderArray = StringUtils.split(page.getOrder(), ',');
Assert.isTrue(orderByArray.length == orderArray.length,
"分页多重排序参数中, 排序字段与排序方向的个数不相等");
for (int i = 0; i < orderByArray.length; i++) {
if (Page.ASC.equals(orderArray[i])) {
criteria.addOrder(Order.asc(orderByArray[i]));
} else {
criteria.addOrder(Order.desc(orderByArray[i]));
}
}
}
return criteria;
}
/**
* 执行 count 查询获得本次 Hql 查询所能获得的对象总数.<br>
* 注意本函数只能自动处理简单的 hql 语句, 复杂的 hql 查询请另行编写 count 语句查询.
*
* @param hql
* @param values
* @return
*/
protected long countHqlResult(final String hql, final Object... values) {
String countHql = prepareCountHql(hql);
try {
Long count = findUnique(countHql, values);
return count;
} catch (Exception e) {
throw new RuntimeException("hql can't be auto count, hql is:"
+ countHql, e);
}
}
/**
* 执行 count 查询获得本次 Hql 查询所能获得的对象总数.<br>
* 注意本函数只能自动处理简单的 hql 语句, 复杂的 hql 查询请另行编写 count 语句查询.
*
* @param hql
* @param values
* @return
*/
protected long countHqlResult(final String hql, final Map<String, ?> values) {
String countHql = prepareCountHql(hql);
try {
Long count = findUnique(countHql, values);
return count;
} catch (Exception e) {
throw new RuntimeException("hql can't be auto count, hql is:"
+ countHql, e);
}
}
/**
* 为 count 查询获得 Hql 查询获得的对象总数做预处理.
*
* @param orgHql
* @return
*/
private String prepareCountHql(String orgHql) {
String fromHql = orgHql;
/*
* select 子句与 order by 子句会影响 count 查询, 进行简单的排除. 比如:
* select name from order as o order by o.number asc -> from order as o
*/
fromHql = "from " + StringUtils.substringAfter(fromHql, "from");
fromHql = StringUtils.substringBefore(fromHql, "order by");
String countHql = "select count(*) " + fromHql;
return countHql;
}
/**
* 执行 count 查询获得本次 Criteria 查询所能获得的对象总数.
*
* @param criteria
* @return
*/
@SuppressWarnings("unchecked")
protected long countCriteriaResult(final Criteria criteria) {
CriteriaImpl impl = (CriteriaImpl) criteria;
// 先把 Projection、ResultTransformer、OrderBy 取出来, 清空三者后再执行 Count 操作
Projection projection = impl.getProjection();
ResultTransformer transformer = impl.getResultTransformer();
List<CriteriaImpl.OrderEntry> orderEntries = null;
try {
orderEntries = (List) ReflectionUtil.getFieldValue(impl, "orderEntries");
ReflectionUtil.setFieldValue(impl, "orderEntries", new ArrayList());
} catch (Exception e) {
logger.error("exception could not be happened", e.getMessage());
}
// 执行 Count 查询
Long totalCountObject = (Long) criteria.setProjection(Projections.rowCount())
.setMaxResults(1).uniqueResult();
long totalCount = (totalCountObject != null) ? totalCountObject : 0;
// 将之前的 Projection, ResultTransformer 和 OrderBy 条件重新设回去
criteria.setProjection(projection);
if (projection == null) {
criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
}
if (transformer != null) {
criteria.setResultTransformer(transformer);
}
try {
ReflectionUtil.setFieldValue(impl, "orderEntries", orderEntries);
} catch (Exception e) {
logger.error("exception could not be happened", e.getMessage());
}
return totalCount;
}
/**
* 按属性查找对象列表, 支持多种匹配方式.
*
* @param matchType
* 匹配方式, 目前支持的取值见 {@link PropertyFilter} 的 MatcheType enum
*/
public List<T> findBy(final String propertyName, final Object value,
final MatchType matchType) {
Criterion criterion = buildCriterion(propertyName, value, matchType);
return find(criterion);
}
/**
* 按属性过滤条件列表查找对象列表.
*
* @param filters
* @return
*/
public List<T> find(List<PropertyFilter> filters) {
Criterion[] criterions = buildCriterionByPropertyFilter(filters);
return find(criterions);
}
/**
* 按属性过滤条件列表分页查找对象.
*
* @param page
* @param filters
* @return
*/
public Page<T> findPage(final Page<T> page, final List<PropertyFilter> filters) {
Criterion[] criterions = buildCriterionByPropertyFilter(filters);
return findPage(page, criterions);
}
/**
* 按属性条件参数创建 Criterion, 辅助函数.
*
* @param propertyName
* @param propertyValue
* @param matchType
* @return
*/
protected Criterion buildCriterion(final String propertyName,
final Object propertyValue, final MatchType matchType) {
Assert.hasText(propertyName, "propertyName can not be null");
Criterion criterion = null;
// 根据 MatchType 构造 criterion
switch (matchType) {
case EQ:
criterion = Restrictions.eq(propertyName, propertyValue);
break;
case LIKE:
criterion = Restrictions.like(propertyName, (String) propertyValue,
MatchMode.ANYWHERE);
break;
case LE:
criterion = Restrictions.le(propertyName, propertyValue);
break;
case LT:
criterion = Restrictions.lt(propertyName, propertyValue);
break;
case GE:
criterion = Restrictions.ge(propertyName, propertyValue);
break;
case GT:
criterion = Restrictions.gt(propertyName, propertyValue);
}
return criterion;
}
/**
* 按属性条件列表创建 Criterion 数组, 辅助函数.
*
* @param filters
* @return
*/
protected Criterion[] buildCriterionByPropertyFilter(
final List<PropertyFilter> filters) {
List<Criterion> criterionList = new ArrayList<Criterion>();
for (PropertyFilter filter : filters) {
if (!filter.hasMultiProperties()) {
// 只有一个属性需要比较的情况
Criterion criterion = buildCriterion(filter.getPropertyName(),
filter.getMatchValue(), filter.getMatchType());
criterionList.add(criterion);
} else {
// 包含多个属性需要比较的情况,进行or处理
Disjunction disjunction = Restrictions.disjunction();
for (String param : filter.getPropertyNames()) {
Criterion criterion = buildCriterion(param, filter
.getMatchValue(), filter.getMatchType());
disjunction.add(criterion);
}
criterionList.add(disjunction);
}
}
return criterionList.toArray(new Criterion[criterionList.size()]);
}
}
BaseHibernateDao
package org.mysterylab.utopiaframework.core.orm.hibernate3;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.persistence.NonUniqueResultException;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.mysterylab.utopiaframework.util.reflect.ReflectionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
/**
* 封装了 Hibernate3 原生 API 的 DAO 泛型基类. 通过 Hibernate 来操纵对象, 主要是 {@link Session}
* 的一些方法的二次封装和 HQL 与 QBC 的一些简单检索.
*
* @author
* @param <T>
* DAO 操作的对象类型
* @param <PK>
* 主键类型
*/
public class BaseHibernateDao<T, PK extends Serializable> {
protected Logger logger = LoggerFactory.getLogger(getClass());
protected SessionFactory sessionFactory;
protected Class<T> entityClass;
/**
* 通过子类的泛型定义获得对象的类型. 比如:
* <pre>
* {@code
* public class UserDao extends BaseHibernateDao<User, Long>
* }
* </pre>
*/
public BaseHibernateDao() {
this.entityClass = ReflectionUtil.getSuperClassGenericType(getClass());
}
/**
* 跳过 DAO 层, 直接在 Service 层使用 BaseHibernateDao 的构造函数,
* 在构造函数中定义对象类型.
* <pre>
* {@code
* BaseHibernateDao<User, Long> userDao = new BaseHibernateDao<User, Long>(sessionFactory, User.class);
* }
* </pre>
* @param sessionFactory
* @param entityClass
*/
public BaseHibernateDao(final SessionFactory sessionFactory,
final Class<T> entityClass) {
this.sessionFactory = sessionFactory;
this.entityClass = entityClass;
}
public SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* 当配置有 sessionFactory 的时候采用 Spring 的自动注入.
*
* @param sessionFactory
*/
@Autowired
public void setSessionFactory(final SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* 获取 Hibernate 的 Session. 注意在使用 {@link SessionFactory#getCurrentSession()}
* 方法的时候, Hibernate 的操作默认必须包含在一个 Transaction 中. 如果这些操作 CRUD
* 操作不被包含在一个具体的事务中, 则会抛出如下异常:
* <pre>
* org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
* </pre>
* 这里顺便说下 {@link SessionFactory#openSession()} 方法与其的区别: opensSession()
* 得到一个新的 session, 而 getCurrentSession() 则是从当前线程中得到事务开始时创建的
* Transaction 的那个 session, 如果事务没有正确启动的话, 就没有一个 session 被绑定到当前线程
*
* @return
*/
public Session getSession() {
return sessionFactory.getCurrentSession();
}
/**
* 取得对象的主键名.
*
* @return
*/
public String getIdName() {
ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
return meta.getIdentifierPropertyName();
}
/**
* 按 id 获取对象. 采用 {@link Session#load(Class, Serializable)} 方法在数据库中不存在与
* OID 对应的记录的时候, 会抛出 {@link ObjectNotFoundException} 的异常. 采用 get()
* 还是采用 load() 方法的区别在于前者是立即加载, 而后者是延迟加载, 使用的场合有:
* <ul>
* <li>1) 如果加载一个对象的目的是为了访问它的各个属性, 用 get() 方法.</li>
* <li>2) 如果加载一个对象的目的是为了删除它, 或者是为了建立与别的对象的关联关系, 用 load() 方法.</li>
* </ul>
* 比如以下代码:
* <pre>
* Transaction tx = session.beginTransaction();
* // 立即检索策略
* Order order = (Order) session.get(Order.class, new Long(1));
* // 延迟检索策略
* Customer customer = (Customer) session.load(Customer.class, new Long(1));
* // 建立 Ordre 与 Customer 的多对一单向关联关系
* order.setCustomer(customer);
* tx.commit();
* // Session 不需要知道 Customer 的各个属性的值, 而只要知道 Customer 对象的 OID 即可生成如下 sql 语句:
* // update ORDER set CUSTOMER_ID=1, ORDER_NUMBER=... where ID=1;
* </pre>
*
* @param id
* 主键
* @return
* @see #get(Serializable)
* @see #delete(Serializable)
*/
@SuppressWarnings("unchecked")
public T load(final PK id) {
Assert.notNull(id, "id can not be null");
return (T) getSession().load(entityClass, id);
}
/**
* 按 id 获取对象. 采用 {@link Session#get(Class, Serializable)} 方法在数据库不存在与
* OID 对应的记录的时候, 会返回 null.
*
* @param id
* 主键
* @return
* @see #load(Serializable)
*/
@SuppressWarnings("unchecked")
public T get(final PK id) {
Assert.notNull(id, "id can not be null");
return (T) getSession().get(entityClass, id);
}
/**
* 按 id 列表获取对象列表.
*
* @param ids
* @return
*/
public List<T> get(final Collection<PK> ids) {
return find(Restrictions.in(getIdName(), ids));
}
/**
* 获取全部对象.
*
* @return
*/
public List<T> getAll() {
return find();
}
/**
* 获取全部对象, 支持按属性排序. 采用 QBC 的检索方式.
*
* @param orderByProperty
* @param isAsc
* @return
*/
@SuppressWarnings("unchecked")
public List<T> getAll(String orderByProperty, boolean isAsc) {
Criteria c = createCriteria();
if (isAsc) {
c.addOrder(Order.asc(orderByProperty));
} else {
c.addOrder(Order.desc(orderByProperty));
}
return c.list();
}
/**
* 按属性查找对象列表, 匹配方式为相等. 采用 QBC 的检索方式.
*
* @param propertyName
* @param value
* @return
*/
public List<T> findBy(final String propertyName, final Object value) {
Assert.hasText(propertyName, "propertyName can not be null");
Criterion criterion = Restrictions.eq(propertyName, value);
return find(criterion);
}
/**
* 采用 QBC 的检索方式查询对象列表.
*
* @param criterions
* 数量可变的 Criterion
*/
@SuppressWarnings("unchecked")
public List<T> find(final Criterion... criterions) {
return createCriteria(criterions).list();
}
/**
* 采用 QBC 的检索方式查询唯一对象. 可能有以下几种情况:
* <ul>
* <li>1) 如果有多个值抛 {@link NonUniqueResultException} 异常, 需要用 setMaxResults(1) 方法来限制.</li>
* <li>2) 如果有值且只有一个, 返回一个 Object.</li>
* <li>3) 如果没值, 返回 null.</li>
* </ul>
*
* @param criterions
* 数量可变的 Criterion
*/
@SuppressWarnings("unchecked")
public T findUnique(final Criterion... criterions) {
return (T) createCriteria(criterions).setMaxResults(1).uniqueResult();
}
/**
* 按属性查找唯一对象, 匹配方式为相等. 采用 QBC 的检索方式.
*
* @param propertyName
* @param value
* @return
* @see #findUnique(Criterion...)
*/
@SuppressWarnings("unchecked")
public T findUniqueBy(final String propertyName, final Object value) {
Assert.hasText(propertyName, "propertyName can not be null");
Criterion criterion = Restrictions.eq(propertyName, value);
return (T) createCriteria(criterion).setMaxResults(1).uniqueResult();
}
/**
* 采用 HQL 的检索方式查询对象列表.
*
* @param values
* 数量可变的参数, 按顺序绑定
*/
@SuppressWarnings("unchecked")
public <X> List<X> find(final String hql, final Object... values) {
return createQuery(hql, values).list();
}
/**
* 采用 HQL 的检索方式查询对象列表.
*
* @param values
* 命名参数, 按名称绑定
*/
@SuppressWarnings("unchecked")
public <X> List<X> find(final String hql, final Map<String, ?> values) {
return createQuery(hql, values).list();
}
/**
* 采用 HQL 的检索方式查询唯一对象.
*
* @param values
* 数量可变的参数, 按顺序绑定
* @see #findUnique(Criterion...)
*/
@SuppressWarnings("unchecked")
public <X> X findUnique(final String hql, final Object... values) {
return (X) createQuery(hql, values).setMaxResults(1).uniqueResult();
}
/**
* 采用 HQL 的检索方式查询唯一对象.
*
* @param values
* 命名参数, 按名称绑定
* @see #findUnique(Criterion...)
*/
@SuppressWarnings("unchecked")
public <X> X findUnique(final String hql, final Map<String, ?> values) {
return (X) createQuery(hql, values).setMaxResults(1).uniqueResult();
}
/**
* 保存新增或修改的对象.
*
* @param entity
* 对象
*/
public void save(final T entity) {
Assert.notNull(entity, "entity can not be null");
getSession().saveOrUpdate(entity);
logger.debug("save entity: {}", entity);
}
/**
* 删除对象.
*
* @param entity
* 对象必须是 session 中的对象或含 id 属性的 transient 对象
*/
public void delete(final T entity) {
Assert.notNull(entity, "entity can not be null");
getSession().delete(entity);
logger.debug("delete entity: {}", entity);
}
/**
* 按 id 删除对象.
*
* @param id
* 主键
* @see #load(Serializable)
*/
public void delete(final PK id) {
Assert.notNull(id, "id can not be null");
delete(load(id));
logger.debug("delete entity {},id is {}", entityClass.getSimpleName(), id);
}
/**
* 采用 HQL 的检索方式进行批量修改/删除操作.
*
* @param values
* 数量可变的参数,按顺序绑定
* @return 更新记录数.
*/
public int batchExecute(final String hql, final Object... values) {
return createQuery(hql, values).executeUpdate();
}
/**
* 采用 HQL 的检索方式进行批量修改/删除操作.
*
* @param values
* 命名参数,按名称绑定
* @return 更新记录数.
*/
public int batchExecute(final String hql, final Map<String, ?> values) {
return createQuery(hql, values).executeUpdate();
}
/**
* 根据查询 HQL 与参数列表创建 Query 对象. 与 find() 函数可进行更加灵活的操作. 比如:
* <pre>
* Query query = session.createQuery("from Order o where o.customer=":customer and "
* + "o.orderNumber like :orderNumber");
* query.setParameter(1, customer);
* query.setParameter(2, orderNumber);
* </pre>
* 上面的程序默认是使用了 Hibernate 的自动根据参数值的 Java 类型来进行对应的映射类型,
* 这样可以减少在第三个参数中指定 Java 类型的麻烦. 但是对于日期的 java.util.Date 类型,
* 会对应多种 Hibernate 映射类型, 如 Hibernate.DATE 或 Hibernate.TIMESTAMP,
* 因此必须在 setParameter() 方法中显式地指定到底对应那种 Hibernate 映射类型. 比如:
* <pre>
* Query query = session.createQuery("from Customer c where c.birthday=:birthday");
* query.setParameter("birthday", birthday, Hibernate.DATE);
* </pre>
*
* @param values
* 数量可变的参数, 按顺序绑定
*/
public Query createQuery(final String queryString, final Object... values) {
Assert.hasText(queryString, "queryString can not be null");
Query query = getSession().createQuery(queryString);
if (values != null) {
for (int i = 0; i < values.length; i++) {
// 按顺序绑定参数
query.setParameter(i, values[i]);
}
}
return query;
}
/**
* 根据查询 HQL 与参数列表创建 Query 对象. 与 find() 函数可进行更加灵活的操作. 比如:
* <pre>
* Query query = session.createQuery("from Customer as c where c.name=:name "
* + "and c.age=:age";
* Customer customer = new Customer();
* customer.setName("Tom");
* customer.setAge(21);
* // 命名参数中的 "name" 和 "age" 分别对应 Customer 类的 name 属性和 age 属性, 否则会抛异常
* query.setProperties(customer);
* </pre>
* 在本方法中采用的是传递一个 {@link Map} 作为参数, 同样会根据 Map 的名来进行自动绑定. 另外,
* setProperties() 方法调用 setParameter() 方法, setParameter() 方法再根据 Customer
* 对象的属性的 Java 类型来判断 Hibernate 映射类型. 如果命名参数为日期类型, 则不能通过
* setProperties() 方法来绑定. 另外, 参数绑定对 null 是安全的, 比如如下代码不会抛异常:
* <pre>
* String name = null;
* session.createQuery("from Customer c where c.name=:name").setString("name", name).list();
* </pre>
* 上面的查询语句对应的 SQL 语句是:
* <pre>
* select * from CUSTOMERS where NMAE=null;
* </pre>
* 这条查询语句的查询结果永远为空. 如果要查询名字为 null 的客户, 应该使用 "is null"
* 比较运算符, 比如:
* <pre>
* // HQL 检索方式
* session.createQuery("from Customer c where c.name is null");
* // QBC 检索方式
* session.createCriteria(Customer.class).add(Restrictions.isNull("name"));
* </pre>
*
* @param values
* 命名参数, 按名称绑定
* @see #find(String, Object...)
*/
public Query createQuery(final String queryString, final Map<String, ?> values) {
Assert.hasText(queryString, "queryString can not be null");
Query query = getSession().createQuery(queryString);
if (values != null) {
// 把命名参数与对象的属性值进行绑定
query.setProperties(values);
}
return query;
}
/**
* 根据 Criterion 条件创建 Criteria. 与 find() 函数可进行更加灵活的操作.
*
* @param criterions
* 数量可变的 Criterion
*/
public Criteria createCriteria(final Criterion... criterions) {
Criteria criteria = getSession().createCriteria(entityClass);
for (Criterion c : criterions) {
criteria.add(c);
}
return criteria;
}
/**
* 初始化对象. 使用 load() 方法得到的仅是对象 Proxy, 在传到 View 层前需要进行初始化.
* 如果传入 entity, 则只初始化 entity 的直接属性, 但不会初始化延迟加载的关联集合和属性.
* 如需初始化关联属性,需执行:
* <pre>
* Hibernate.initialize(user.getRoles()) // 初始化User的直接属性和关联集合
* Hibernate.initialize(user.getDescription()) // 初始化User的直接属性和延迟加载的Description属性
* </pre>
*/
public void initProxyObject(Object proxy) {
Hibernate.initialize(proxy);
}
/**
* 为 Query 添加 distinct transformer. 预加载关联对象的 HQL 会引起主对象重复,
* 需要进行 distinct 处理.
*
* @param query
* @return
*/
public Query distinct(Query query) {
query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
return query;
}
/**
* 为 Criteria 添加 distinct transformer. 预加载关联对象的 HQL 会引起主对象重复,
* 需要进行 distinct 处理.
*
* @param criteria
* @return
*/
public Criteria distinct(Criteria criteria) {
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
return criteria;
}
/**
* 判断对象的属性值在数据库内是否唯一.<br>
* 在修改对象的情景下, 如果属性新修改的值 (value) 等于属性原来的值 (orgValue) 则不作比较.
*
* @param propertyName
* @param newValue
* @param oldValue
* @return
*/
public boolean isPropertyUnique(final String propertyName,
final Object newValue, final Object oldValue) {
if (newValue == null || newValue.equals(oldValue)) {
return true;
}
Object object = findUniqueBy(propertyName, newValue);
return (object == null);
}
/**
* 清除当前 Session.
*/
public void flush() {
getSession().flush();
}
}
Dao使用说明
package org.mysterylab.utopiaframework.core.orm.hibernate3;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.persistence.NonUniqueResultException;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.mysterylab.utopiaframework.util.reflect.ReflectionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
/**
* 封装了 Hibernate3 原生 API 的 DAO 泛型基类. 通过 Hibernate 来操纵对象, 主要是 {@link Session}
* 的一些方法的二次封装和 HQL 与 QBC 的一些简单检索.
*
* @author
* @param <T>
* DAO 操作的对象类型
* @param <PK>
* 主键类型
*/
public class BaseHibernateDao<T, PK extends Serializable> {
protected Logger logger = LoggerFactory.getLogger(getClass());
protected SessionFactory sessionFactory;
protected Class<T> entityClass;
/**
* 通过子类的泛型定义获得对象的类型. 比如:
* <pre>
* {@code
* public class UserDao extends BaseHibernateDao<User, Long>
* }
* </pre>
*/
public BaseHibernateDao() {
this.entityClass = ReflectionUtil.getSuperClassGenericType(getClass());
}
/**
* 跳过 DAO 层, 直接在 Service 层使用 BaseHibernateDao 的构造函数,
* 在构造函数中定义对象类型.
* <pre>
* {@code
* BaseHibernateDao<User, Long> userDao = new BaseHibernateDao<User, Long>(sessionFactory, User.class);
* }
* </pre>
* @param sessionFactory
* @param entityClass
*/
public BaseHibernateDao(final SessionFactory sessionFactory,
final Class<T> entityClass) {
this.sessionFactory = sessionFactory;
this.entityClass = entityClass;
}
public SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* 当配置有 sessionFactory 的时候采用 Spring 的自动注入.
*
* @param sessionFactory
*/
@Autowired
public void setSessionFactory(final SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* 获取 Hibernate 的 Session. 注意在使用 {@link SessionFactory#getCurrentSession()}
* 方法的时候, Hibernate 的操作默认必须包含在一个 Transaction 中. 如果这些操作 CRUD
* 操作不被包含在一个具体的事务中, 则会抛出如下异常:
* <pre>
* org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
* </pre>
* 这里顺便说下 {@link SessionFactory#openSession()} 方法与其的区别: opensSession()
* 得到一个新的 session, 而 getCurrentSession() 则是从当前线程中得到事务开始时创建的
* Transaction 的那个 session, 如果事务没有正确启动的话, 就没有一个 session 被绑定到当前线程
*
* @return
*/
public Session getSession() {
return sessionFactory.getCurrentSession();
}
/**
* 取得对象的主键名.
*
* @return
*/
public String getIdName() {
ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
return meta.getIdentifierPropertyName();
}
/**
* 按 id 获取对象. 采用 {@link Session#load(Class, Serializable)} 方法在数据库中不存在与
* OID 对应的记录的时候, 会抛出 {@link ObjectNotFoundException} 的异常. 采用 get()
* 还是采用 load() 方法的区别在于前者是立即加载, 而后者是延迟加载, 使用的场合有:
* <ul>
* <li>1) 如果加载一个对象的目的是为了访问它的各个属性, 用 get() 方法.</li>
* <li>2) 如果加载一个对象的目的是为了删除它, 或者是为了建立与别的对象的关联关系, 用 load() 方法.</li>
* </ul>
* 比如以下代码:
* <pre>
* Transaction tx = session.beginTransaction();
* // 立即检索策略
* Order order = (Order) session.get(Order.class, new Long(1));
* // 延迟检索策略
* Customer customer = (Customer) session.load(Customer.class, new Long(1));
* // 建立 Ordre 与 Customer 的多对一单向关联关系
* order.setCustomer(customer);
* tx.commit();
* // Session 不需要知道 Customer 的各个属性的值, 而只要知道 Customer 对象的 OID 即可生成如下 sql 语句:
* // update ORDER set CUSTOMER_ID=1, ORDER_NUMBER=... where ID=1;
* </pre>
*
* @param id
* 主键
* @return
* @see #get(Serializable)
* @see #delete(Serializable)
*/
@SuppressWarnings("unchecked")
public T load(final PK id) {
Assert.notNull(id, "id can not be null");
return (T) getSession().load(entityClass, id);
}
/**
* 按 id 获取对象. 采用 {@link Session#get(Class, Serializable)} 方法在数据库不存在与
* OID 对应的记录的时候, 会返回 null.
*
* @param id
* 主键
* @return
* @see #load(Serializable)
*/
@SuppressWarnings("unchecked")
public T get(final PK id) {
Assert.notNull(id, "id can not be null");
return (T) getSession().get(entityClass, id);
}
/**
* 按 id 列表获取对象列表.
*
* @param ids
* @return
*/
public List<T> get(final Collection<PK> ids) {
return find(Restrictions.in(getIdName(), ids));
}
/**
* 获取全部对象.
*
* @return
*/
public List<T> getAll() {
return find();
}
/**
* 获取全部对象, 支持按属性排序. 采用 QBC 的检索方式.
*
* @param orderByProperty
* @param isAsc
* @return
*/
@SuppressWarnings("unchecked")
public List<T> getAll(String orderByProperty, boolean isAsc) {
Criteria c = createCriteria();
if (isAsc) {
c.addOrder(Order.asc(orderByProperty));
} else {
c.addOrder(Order.desc(orderByProperty));
}
return c.list();
}
/**
* 按属性查找对象列表, 匹配方式为相等. 采用 QBC 的检索方式.
*
* @param propertyName
* @param value
* @return
*/
public List<T> findBy(final String propertyName, final Object value) {
Assert.hasText(propertyName, "propertyName can not be null");
Criterion criterion = Restrictions.eq(propertyName, value);
return find(criterion);
}
/**
* 采用 QBC 的检索方式查询对象列表.
*
* @param criterions
* 数量可变的 Criterion
*/
@SuppressWarnings("unchecked")
public List<T> find(final Criterion... criterions) {
return createCriteria(criterions).list();
}
/**
* 采用 QBC 的检索方式查询唯一对象. 可能有以下几种情况:
* <ul>
* <li>1) 如果有多个值抛 {@link NonUniqueResultException} 异常, 需要用 setMaxResults(1) 方法来限制.</li>
* <li>2) 如果有值且只有一个, 返回一个 Object.</li>
* <li>3) 如果没值, 返回 null.</li>
* </ul>
*
* @param criterions
* 数量可变的 Criterion
*/
@SuppressWarnings("unchecked")
public T findUnique(final Criterion... criterions) {
return (T) createCriteria(criterions).setMaxResults(1).uniqueResult();
}
/**
* 按属性查找唯一对象, 匹配方式为相等. 采用 QBC 的检索方式.
*
* @param propertyName
* @param value
* @return
* @see #findUnique(Criterion...)
*/
@SuppressWarnings("unchecked")
public T findUniqueBy(final String propertyName, final Object value) {
Assert.hasText(propertyName, "propertyName can not be null");
Criterion criterion = Restrictions.eq(propertyName, value);
return (T) createCriteria(criterion).setMaxResults(1).uniqueResult();
}
/**
* 采用 HQL 的检索方式查询对象列表.
*
* @param values
* 数量可变的参数, 按顺序绑定
*/
@SuppressWarnings("unchecked")
public <X> List<X> find(final String hql, final Object... values) {
return createQuery(hql, values).list();
}
/**
* 采用 HQL 的检索方式查询对象列表.
*
* @param values
* 命名参数, 按名称绑定
*/
@SuppressWarnings("unchecked")
public <X> List<X> find(final String hql, final Map<String, ?> values) {
return createQuery(hql, values).list();
}
/**
* 采用 HQL 的检索方式查询唯一对象.
*
* @param values
* 数量可变的参数, 按顺序绑定
* @see #findUnique(Criterion...)
*/
@SuppressWarnings("unchecked")
public <X> X findUnique(final String hql, final Object... values) {
return (X) createQuery(hql, values).setMaxResults(1).uniqueResult();
}
/**
* 采用 HQL 的检索方式查询唯一对象.
*
* @param values
* 命名参数, 按名称绑定
* @see #findUnique(Criterion...)
*/
@SuppressWarnings("unchecked")
public <X> X findUnique(final String hql, final Map<String, ?> values) {
return (X) createQuery(hql, values).setMaxResults(1).uniqueResult();
}
/**
* 保存新增或修改的对象.
*
* @param entity
* 对象
*/
public void save(final T entity) {
Assert.notNull(entity, "entity can not be null");
getSession().saveOrUpdate(entity);
logger.debug("save entity: {}", entity);
}
/**
* 删除对象.
*
* @param entity
* 对象必须是 session 中的对象或含 id 属性的 transient 对象
*/
public void delete(final T entity) {
Assert.notNull(entity, "entity can not be null");
getSession().delete(entity);
logger.debug("delete entity: {}", entity);
}
/**
* 按 id 删除对象.
*
* @param id
* 主键
* @see #load(Serializable)
*/
public void delete(final PK id) {
Assert.notNull(id, "id can not be null");
delete(load(id));
logger.debug("delete entity {},id is {}", entityClass.getSimpleName(), id);
}
/**
* 采用 HQL 的检索方式进行批量修改/删除操作.
*
* @param values
* 数量可变的参数,按顺序绑定
* @return 更新记录数.
*/
public int batchExecute(final String hql, final Object... values) {
return createQuery(hql, values).executeUpdate();
}
/**
* 采用 HQL 的检索方式进行批量修改/删除操作.
*
* @param values
* 命名参数,按名称绑定
* @return 更新记录数.
*/
public int batchExecute(final String hql, final Map<String, ?> values) {
return createQuery(hql, values).executeUpdate();
}
/**
* 根据查询 HQL 与参数列表创建 Query 对象. 与 find() 函数可进行更加灵活的操作. 比如:
* <pre>
* Query query = session.createQuery("from Order o where o.customer=":customer and "
* + "o.orderNumber like :orderNumber");
* query.setParameter(1, customer);
* query.setParameter(2, orderNumber);
* </pre>
* 上面的程序默认是使用了 Hibernate 的自动根据参数值的 Java 类型来进行对应的映射类型,
* 这样可以减少在第三个参数中指定 Java 类型的麻烦. 但是对于日期的 java.util.Date 类型,
* 会对应多种 Hibernate 映射类型, 如 Hibernate.DATE 或 Hibernate.TIMESTAMP,
* 因此必须在 setParameter() 方法中显式地指定到底对应那种 Hibernate 映射类型. 比如:
* <pre>
* Query query = session.createQuery("from Customer c where c.birthday=:birthday");
* query.setParameter("birthday", birthday, Hibernate.DATE);
* </pre>
*
* @param values
* 数量可变的参数, 按顺序绑定
*/
public Query createQuery(final String queryString, final Object... values) {
Assert.hasText(queryString, "queryString can not be null");
Query query = getSession().createQuery(queryString);
if (values != null) {
for (int i = 0; i < values.length; i++) {
// 按顺序绑定参数
query.setParameter(i, values[i]);
}
}
return query;
}
/**
* 根据查询 HQL 与参数列表创建 Query 对象. 与 find() 函数可进行更加灵活的操作. 比如:
* <pre>
* Query query = session.createQuery("from Customer as c where c.name=:name "
* + "and c.age=:age";
* Customer customer = new Customer();
* customer.setName("Tom");
* customer.setAge(21);
* // 命名参数中的 "name" 和 "age" 分别对应 Customer 类的 name 属性和 age 属性, 否则会抛异常
* query.setProperties(customer);
* </pre>
* 在本方法中采用的是传递一个 {@link Map} 作为参数, 同样会根据 Map 的名来进行自动绑定. 另外,
* setProperties() 方法调用 setParameter() 方法, setParameter() 方法再根据 Customer
* 对象的属性的 Java 类型来判断 Hibernate 映射类型. 如果命名参数为日期类型, 则不能通过
* setProperties() 方法来绑定. 另外, 参数绑定对 null 是安全的, 比如如下代码不会抛异常:
* <pre>
* String name = null;
* session.createQuery("from Customer c where c.name=:name").setString("name", name).list();
* </pre>
* 上面的查询语句对应的 SQL 语句是:
* <pre>
* select * from CUSTOMERS where NMAE=null;
* </pre>
* 这条查询语句的查询结果永远为空. 如果要查询名字为 null 的客户, 应该使用 "is null"
* 比较运算符, 比如:
* <pre>
* // HQL 检索方式
* session.createQuery("from Customer c where c.name is null");
* // QBC 检索方式
* session.createCriteria(Customer.class).add(Restrictions.isNull("name"));
* </pre>
*
* @param values
* 命名参数, 按名称绑定
* @see #find(String, Object...)
*/
public Query createQuery(final String queryString, final Map<String, ?> values) {
Assert.hasText(queryString, "queryString can not be null");
Query query = getSession().createQuery(queryString);
if (values != null) {
// 把命名参数与对象的属性值进行绑定
query.setProperties(values);
}
return query;
}
/**
* 根据 Criterion 条件创建 Criteria. 与 find() 函数可进行更加灵活的操作.
*
* @param criterions
* 数量可变的 Criterion
*/
public Criteria createCriteria(final Criterion... criterions) {
Criteria criteria = getSession().createCriteria(entityClass);
for (Criterion c : criterions) {
criteria.add(c);
}
return criteria;
}
/**
* 初始化对象. 使用 load() 方法得到的仅是对象 Proxy, 在传到 View 层前需要进行初始化.
* 如果传入 entity, 则只初始化 entity 的直接属性, 但不会初始化延迟加载的关联集合和属性.
* 如需初始化关联属性,需执行:
* <pre>
* Hibernate.initialize(user.getRoles()) // 初始化User的直接属性和关联集合
* Hibernate.initialize(user.getDescription()) // 初始化User的直接属性和延迟加载的Description属性
* </pre>
*/
public void initProxyObject(Object proxy) {
Hibernate.initialize(proxy);
}
/**
* 为 Query 添加 distinct transformer. 预加载关联对象的 HQL 会引起主对象重复,
* 需要进行 distinct 处理.
*
* @param query
* @return
*/
public Query distinct(Query query) {
query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
return query;
}
/**
* 为 Criteria 添加 distinct transformer. 预加载关联对象的 HQL 会引起主对象重复,
* 需要进行 distinct 处理.
*
* @param criteria
* @return
*/
public Criteria distinct(Criteria criteria) {
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
return criteria;
}
/**
* 判断对象的属性值在数据库内是否唯一.<br>
* 在修改对象的情景下, 如果属性新修改的值 (value) 等于属性原来的值 (orgValue) 则不作比较.
*
* @param propertyName
* @param newValue
* @param oldValue
* @return
*/
public boolean isPropertyUnique(final String propertyName,
final Object newValue, final Object oldValue) {
if (newValue == null || newValue.equals(oldValue)) {
return true;
}
Object object = findUniqueBy(propertyName, newValue);
return (object == null);
}
/**
* 清除当前 Session.
*/
public void flush() {
getSession().flush();
}
}
[/size]