package com.code.util;
import static java.util.Locale.ENGLISH;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.jdbc.Work;
import org.springframework.orm.hibernate4.support.HibernateDaoSupport;
/**
*
* @author LGF 2014-11-17
* BaseHibernateDAO 工具类
* @param <T> 类型
*/
@SuppressWarnings("all")
public class HibernateDAO<T> extends HibernateDaoSupport {
//统计条数语句
private String countHql;
//基本的 hql 语句
private String baseHql;
//统计条数,条件验证正则表达式
private final static String WHERE_CHECK = ".+[\\s]+[w|W][h|H][e|E][r|R][e|E]+[\\s]+.*";
//hql语句验证正则表达式
private final static String HQL_QUERY = ".*\\s+.+";
//类型
private Class<T> type;
public HibernateDAO(){
initial();
}
//初始化方法
private void initial(){
//获取类型
type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
if(type == null){
new RuntimeException("ERROR: [ the <T> type is null ]");
}else{
countHql = "SELECT COUNT(1) FROM "+ type.getName();
baseHql = "FROM " + type.getName();
}
}
/**
* 获取session
* @return Session
*/
protected Session getSession(){
return super.getSessionFactory().getCurrentSession();
}
//----------------------------------------------------------- 按 id 查询一个对象 -------------------------------------------
/**
* 按 id 获取一个对象
* @param id
* @return T
*/
protected T get(Serializable id){
return (T) getSession().get(type, id);
}
/**
* 按 id 加载一个对象
* @param id
* @return T
*/
protected T load(Serializable id){
return (T) getSession().load(type, id);
}
//----------------------------------------------------------- 查询一个对象列表 -------------------------------------------
/**
* 查询一个对象列表
* @param nameOrHQL
* <p style="color:red">
* 查询名称或者HQL
* 如果传的是配置文件中的 SQL|HQL 名称(name) ,
* 中间空不能有空格,前后可以为空格,
* 否则验证错误,会当做HQL语句查询
* </p>
* @param params 参数集合
* @return List<T>
*/
protected List<T> query(String nameOrHQL,Object... params){
return query(nameOrHQL,null,params);
}
/**
* 查询一个对象列表
* @param nameOrHQL
* <p style="color:red">
* 查询名称或者HQL
* 如果传的是配置文件中的 SQL|HQL 名称(name) ,
* 中间空不能有空格,前后可以为空格,
* 否则验证错误,会当做HQL语句查询
* </p>
* @param page 分页工具
* @param params 参数集合
* @return List<T>
*/
protected List<T> query(String nameOrHQL,Page page,Object... params){
return getQuery(nameOrHQL, page,params).list();
}
/**
* 查询一个对象列表
* @return List<T>
*/
protected List<T> query(){
return query(null);
}
/**
* 按页数查询一个对象列表
* @param page 分页工具
* @return List<T>
*/
protected List<T> query(Page page){
return getQuery(baseHql, page).list();
}
//------------------------------------------------ 动态条件查询 -------------------------------------------------
/* and */
/**
* 动态条件查询,如果对象字段有值将会用 and 连接条件查询
* @param obj 传入需要动态查询的类型对象
* @return List<T>
* @throws Exception
*/
protected List<T> queryByCriteriaAnd(T obj) throws Exception{
return queryByCriteriaAnd(obj,null);
}
/**
* 动态条件查询,如果对象字段有值将会用 and 连接条件查询
* @param obj 传入需要动态查询的类型对象
* @param page 分页工具
* @return List<T>
* @throws Exception
*/
protected List<T> queryByCriteriaAnd(T obj,Page page) throws Exception{
Criteria criteria = getSession().createCriteria(type);
Map<String, Object> values = getFieldValues(obj);
for (String key : values.keySet()) {
criteria.add(Restrictions.eq(key, values.get(key)));
}
return setCriteriaPage(criteria,page).list();
}
/* or */
/**
* 动态条件查询,如果对象字段有值将会用 or 连接条件查询
* @param obj 传入需要动态查询的类型对象
* @return List<T>
* @throws Exception
*/
protected List<T> queryByCriteriaOption(T obj) throws Exception{
return queryByCriteriaOption(obj,null);
}
/**
* 动态条件查询,如果对象字段有值将会用 or 连接条件查询
* @param obj 传入需要动态查询的类型对象
* @param page 分页工具
* @return List<T>
* @throws Exception
*/
protected List<T> queryByCriteriaOption(T obj,Page page) throws Exception{
Criteria criteria = getSession().createCriteria(type);
Map<String, Object> values = getFieldValues(obj);
Criterion[] conditions = new Criterion[values.size()];
int index = 0;
for (String key : values.keySet()) {
conditions[index] = Restrictions.eq(key,values.get(key));
index++;
}
criteria.add(Restrictions.disjunction(conditions));
return setCriteriaPage(criteria,page).list();
}
/* like */
/**
* 动态条件查询,如果对象字段有值将会用 like 连接条件查询
* @param obj 传入需要动态查询的类型对象
* @param page 分页工具
* @param ignore 是否忽略大小写
* @param mode 模式
* @return List<T>
* @throws Exception
*/
protected List<T> queryByCriteriaLike(T obj,Page page,boolean ignore,MatchMode mode) throws Exception{
Criteria criteria = getSession().createCriteria(type);
Map<String, Object> values = getFieldValues(obj);
System.out.println(values);
for (String key : values.keySet()) {
if(key.getClass()!=String.class){continue;}
if(ignore){
criteria.add(Restrictions.ilike(key,values.get(key).toString(),mode));
}else{
criteria.add(Restrictions.like(key,values.get(key).toString(),mode));
}
}
return setCriteriaPage(criteria,page).list();
}
/**
* 动态条件查询,如果对象字段有值将会用 like 连接条件查询
* @param obj 传入需要动态查询的类型对象
* @param page 分页工具
* @param ignore 是否忽略大小写
* @return List<T>
* @throws Exception
*/
protected List<T> queryByCriteriaLike(T obj,Page page,boolean ignore) throws Exception{
return queryByCriteriaLike(obj,page,ignore,MatchMode.ANYWHERE);
}
/**
* 动态条件查询,如果对象字段有值将会用 like 连接条件查询
* @param obj 传入需要动态查询的类型对象
* @param page 分页工具
* @param mode 模式
* @return List<T>
* @throws Exception
*/
protected List<T> queryByCriteriaLike(T obj,Page page,MatchMode mode) throws Exception{
return queryByCriteriaLike(obj,page,false,mode);
}
/**
* 动态条件查询,如果对象字段有值将会用 like 连接条件查询
* @param obj 传入需要动态查询的类型对象
* @return List<T>
* @throws Exception
*/
protected List<T> queryByCriteriaLike(T obj) throws Exception{
return queryByCriteriaLike(obj,null,false,MatchMode.ANYWHERE);
}
/* criteria page */
/**
* 设置分页参数
* @param criteria
* @param page 分页对象
* @return Criteria
*/
protected Criteria setCriteriaPage(Criteria criteria,Page page){
if(page!=null){
page.setTotal((long)criteria.setProjection(Projections.rowCount()).uniqueResult());
criteria.setFirstResult(page.getPageSize()*(page.getPageNumber()-1)).setMaxResults(page.getPageSize());
}
return criteria;
}
//------------------------------------------------ 查询一个对象 -----------------------------------------------------
/**
* 查询一个对象
* @param nameOrHQL
* <p style="color:red">
* 查询名称或者HQL
* 如果传的是配置文件中的 SQL|HQL 名称(name) ,
* 中间空不能有空格,前后可以为空格,
* 否则验证错误,会当做HQL语句查询
* </p>
* @param params 参数集合
* @return T
*/
protected T queryUniqueResult(String nameOrHQL,Object... params){
return (T) getQuery(nameOrHQL, params).uniqueResult();
}
//------------------------------------------------ 增删改 -----------------------------------------------------
/**
* 更新一个对象
* @param o 对象
* @throws Exception
*/
protected int update(T transientInstance) throws Exception{
getSession().update(transientInstance);
return 1;
}
/**
* 删除一个对象
* @param o 对象
* @throws Exception
*/
protected int delete(T transientInstance) throws Exception{
getSession().delete(transientInstance);
return 1;
}
/**
* 批量删除操作
* @param transientInstances 删除的对象集合
* @return 更新的记录数
*/
protected int batchDelete(T... transientInstances){
int count = 0;
Session session = getSession();
for (T transientInstance : transientInstances) {
session.delete(transientInstance);
if(count%20 == 0){
session.flush();
session.clear();
}
count++;
}
return count;
}
/**
* 添加一个对象
* @param o 对象
* @throws Exception
*/
protected int save(T transientInstance) throws Exception{
getSession().save(transientInstance);
return 1;
}
/**
* 批量保存操作
* @param transientInstances 删除的对象集合
* @return 更新的记录数
*/
protected int batchSave(T... transientInstances) throws Exception{
int count = 0;
Session session = getSession();
for (T transientInstance : transientInstances) {
session.save(transientInstance);
if(count%20 == 0){
session.flush();
session.clear();
}
count++;
}
return count;
}
/**
* 更新一个对象
* @param nameOrHQL
* <p style="color:red">
* 查询名称或者HQL
* 如果传的是配置文件中的 SQL|HQL 名称(name) ,
* 中间空不能有空格,前后可以为空格,
* 否则验证错误,会当做HQL语句查询
* </p>
* @param params 参数集合
* @return int结果
*/
protected int update(String nameOrHQL,Object... params) throws Exception{
return getQuery(nameOrHQL,params).executeUpdate();
}
//------------------------------------------------ HQL查询参数设置 -----------------------------------------------------
/**
* 设置查询参数
* @param nameOrHQL
* <p style="color:red">
* 查询名称或者HQL
* 如果传的是配置文件中的 SQL|HQL 名称(name) ,
* 中间空不能有空格,前后可以为空格,
* 否则验证错误,会当做HQL语句查询
* </p>
* @param params 参数集合
* @return Query
*/
protected Query getQuery(String nameOrHQL,Object... params){
return getQuery(nameOrHQL,null,params);
}
/**
* 设置 HQL 查询参数
* @param nameOrHQL
* <p style="color:red">
* 查询名称或者HQL
* 如果传的是配置文件中的 SQL|HQL 名称(name) ,
* 中间空不能有空格,前后可以为空格,
* 否则验证错误,会当做HQL语句查询
* </p>
* @param page 分页工具
* @param params 参数集合
* @return Query
*/
protected Query getQuery(String nameOrHQL,Page page,Object... params){
return getQuery(nameOrHQL,page,false,params);
}
/**
* 设置 HQL 查询参数
* @param nameOrHQL
* <p style="color:red">
* 查询名称或者HQL
* 如果传的是配置文件中的 SQL|HQL 名称(name) ,
* 中间空不能有空格,前后可以为空格,
* 否则验证错误,会当做HQL语句查询
* </p>
* @param page 分页工具
* @param params 参数集合
* @return Query
*/
protected Query getQuery(String nameOrHQL,Page page,boolean isNamedParam,Object... params){
Query query = null;
String qs = null;
if(nameOrHQL.trim().matches(HQL_QUERY)){
query = getSession().createQuery(nameOrHQL);
}else{
query = getSession().getNamedQuery(nameOrHQL);
}
qs = query.getQueryString();
int index = 0;
for (Object object : params) {
query.setParameter(index, object);
index++;
}
if(page!=null){
page.setTotal(getCount(qs,params));
query.setFirstResult((page.getPageNumber()-1)*page.getPageSize()).setMaxResults(page.getPageSize());
}
print(qs,params);
return query;
}
/**
* 设置配置文件HQL或者SQL查询
* @param queryString 查询字符串,获取配置文件中的名称
* @param page 分页工具
* @param params 参数集合
* @return Query
*/
protected Query getNamedQuery(String name,Page page,Object... params){
Query query = getSession().getNamedQuery(name);
int index = 0;
for (Object object : params) {
query.setParameter(index, object);
index++;
}
if(page!=null){
page.setTotal(getCount(query.getQueryString(),params));
query.setFirstResult((page.getPageNumber()-1)*page.getPageSize()).setMaxResults(page.getPageSize());
}
print(query.getQueryString(),params);
return query;
}
/**
* 设置配置文件HQL或者SQL查询
* @param queryString 查询字符串,获取配置文件中的名称
* @param params 参数集合
* @return Query
*/
protected Query getNamedQuery(String name,Object... params){
return getNamedQuery(name,null,params);
}
/* 打印 查询语句 和参数 */
private void print(String query,Object... params){
System.out.println("query: [ " + query+" ] params: "+Arrays.asList(params));
}
//------------------------------------------------ SQL查询参数设置 -----------------------------------------------------
/**
* 设置 SQL 查询参数
* @param sql 语句
* @param page 分页工具
* @param params 参数集合
* @return SQLQuery
*/
protected SQLQuery getSQLQuery(String sql,Page page,Class<?> entity,Object... params){
return getSQLQuery(sql,page,entity,false,params);
}
/**
* 设置 SQL 查询参数
* @param sql 语句
* @param page 分页工具
* @param isNamedParam 是否是命名参数 ?
* @param params 参数集合
* @return SQLQuery
*/
protected SQLQuery getSQLQuery(String sql,Page page,Class<?> entity,boolean isNamedParam,Object... params){
SQLQuery query = getSession().createSQLQuery(sql);
int index = 0;
for (Object object : params) {
query.setParameter(index, object);
index++;
}
if(entity!=null){
query.addEntity(entity);
}
return query;
}
//------------------------------------------------原生SQL操作 -----------------------------------------------------
/**
* 原生SQL更新
* @param sql 语句
* @param params 参数集合
* @return 受影响行数
*/
protected int sqlUpdate(String sql,Object...params){
return getSQLQuery(sql,null,null,params).executeUpdate();
}
/* 存储过程 */
/**
* 调用存储过程,默认添加实体类类型:<T>
* @param sql 存储过程名称
* @param params 参数集合
* @return List<T>
*/
protected List<T> callEntity(String sql,Object...params){
return getSQLQuery(sql, null, type, params).list();
}
/**
* <h3>调用存储过程,带返回值的</h3>
* <p style="color:red">注意:存储过程不能有输出参数.有输出参数会报错</p>
* <p style="color:red">输出参数用select把参数查询出来</p>
* <p style="color:red">输入的参数的值不能为空。</p>
* @param sql 存储过程
* @param params 参数集合
* @return List
*/
protected List call(String sql,Object...params){
return getSQLQuery(sql, null, null, params).list();
}
/**
* 默认添加泛型<T>
* @param name
* <p style="color:red">
* 查询名称或者HQL
* 如果传的是配置文件中的 SQL|HQL 名称(name) ,
* 中间空不能有空格,前后可以为空格,
* 否则验证错误,会当做HQL语句查询
* </p>
* @param params 参数集合
* @return List<T>
*/
protected <T> List<T> callNamedEntity(String name,Object...params){
Query query = getQuery(name, params);
if(query instanceof SQLQuery){
((SQLQuery) query).addEntity(type);
}
return query.list();
}
/**
* <h3>调用存储过程,带返回值的</h3>
* <p style="color:red">注意:存储过程不能有输出参数.有输出参数会报错</p>
* <p style="color:red">输出参数用select把参数查询出来</p>
* <p style="color:red">输入的参数的值不能为空。</p>
* @param name
* <p style="color:red">
* 查询名称或者HQL
* 如果传的是配置文件中的 SQL|名称(name) ,
* 中间空不能有空格,前后可以为空格,
* 否则验证错误,会当做HQL语句查询
* </p>
* @param name 参数集合
* @return List
*
*/
protected List callNamed(String name,Object...params){
return getQuery(name, params).list();
}
/**
* JDBC 操作
* @param work
*/
protected void doWork(Work work){
getSession().doWork(work);
}
//------------------------------------------------ 统计条数 -----------------------------------------------------
/**
* 统计条数
* @param page 分页工具
* @return Long 对象
*/
protected Long getCount(String hql,Object... params){
StringBuffer sb = new StringBuffer();
sb.append(countHql)
.append(" ")
.append(getAlias(hql))
.append(" ")
.append(getCondition(hql));
return (Long)getQuery(sb.toString(),params).uniqueResult();
}
/**
* 获取分页查询条件
* @param hql 语句
* @return 条件
*/
private String getCondition(String hql){
int len = checkWhereExist(hql);
if(len != -1){
return hql.substring(len);
}
return "";
}
/**
* 获取别名
* @param hql 语句
* @return 别名
*/
private int checkWhereExist(String hql){
if(hql.matches(WHERE_CHECK)){
return hql.toUpperCase().indexOf("WHERE");
}
return -1;
}
/**
* 获取别名
* @param hql 语句
* @return 别名
*/
private String getAlias(String hql){
String[] hs = hql.split("\\s+");
for (int i = 0; i < hs.length; i++) {
String temp = hs[i].toUpperCase();
if(temp.equals("FROM")&&(i+2)<hs.length){
return hs[i+2].toUpperCase().equals("WHERE")?"":hs[i+2];
};
}
return "";
}
//------------------------------------------------ 其他 -----------------------------------------------------
/**
* 获取字符串(type)类型
* @return
*/
public String getType() {
return type.getName();
}
/**
* 将名称首字母转换成大写
* @param name 转换名称
* @return String
*/
private static String capitalize(String name){
if (name == null || name.trim().length() == 0) {
return name;
}
return name.substring(0, 1).toUpperCase(ENGLISH) + name.substring(1);
}
/**
* 获取类属性get方法
* 没有get方法返回空
* @param cls 类型
* @param field 字段名称
* @return 方法 Method
* @throws NoSuchMethodException
* @throws SecurityException
*/
private static Method getReadMethod(Class<?> cls,Field field){
StringBuffer sb = new StringBuffer();
if(field.getType()==boolean.class){
sb.append("is").append(capitalize(field.getName()));
}else{
sb.append("get").append(capitalize(field.getName()));
}
try {
return cls.getMethod(sb.toString());
} catch (NoSuchMethodException e) {
return null;
} catch (SecurityException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取类属性set方法
* @param cls 类型
* @param field 字段名称
* @return 方法 Method
* @throws NoSuchMethodException
* @throws SecurityException
*/
private static Method getWriterMethod(Class<?> cls,Field field) throws NoSuchMethodException, SecurityException{
return cls.getMethod(new StringBuffer().append("set").append(capitalize(field.getName())).toString(),field.getType());
}
/**
* 获取一个字段的属性值
* 其字段必须有标准的get方法,如果没有返回空
* @param obj 对象
* @param field 字段
* @return 字段属性值
* @throws Exception
*/
private static Object getFieldValue(Object obj,Field field){
try {
Method method = getReadMethod(obj.getClass(), field);
if(method!=null){
return method.invoke(obj);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 传入一个对象,动态取值属性值方法
* 如果该对象的属性值不为空,并且有 标准的 get 方法
* 可以使用 HIBERNATE Criteria实 现动态查询
* @param obj 对象
* @return Map键值对的方式,键:字段名称,值:字段值
* @throws Exception
*/
private static Map<String,Object> getFieldValues(Object obj) throws Exception{
Field[] fs = obj.getClass().getDeclaredFields();
if(fs == null){
return null;
}
Map<String,Object> fields = new HashMap<>();
for (Field field : fs) {
Object value = getFieldValue(obj,field);
if(value == null)continue;
if(value.getClass()==Integer.class){
int val = Integer.parseInt(value.toString());
if(val<=0)continue;
}else if(value.getClass()==String.class){
if(value.toString().trim().length()<=0)continue;
}else if(value instanceof Collection)continue;
fields.put(field.getName(), value);
}
return fields;
}
}