疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

SSH中hibernate窍门总结


 

 
hibernate 就是ORM框架,就是要配置关系外键。 
你不配置你用他干啥,换mybatis得了。 
小系统或大项目中数据量不大的子系统能使用hibernate就用hibernate,方便简单。 
这里我个人有摸索出的窍门,大家可以参考一下。
 
首先,逆向生成hibernate对象配置,保留一对多关系,与之对应的多对一去掉,按需配置懒加载。 
然后DAO可以使用DAO的模板类,贴出我丰富过的模版类。(使用了spring的事务管理),具体业务接口继承这个模版DAO接口,然后可以自定义一些特定的方法,然后具体业务接口的实现类继承模版DAO的实现即可。
 
模版接口类GenericDaoI:
 
package cn.zzy.tm.dao;
 
import java.io.Serializable;
import java.util.List;
 
import org.hibernate.criterion.Criterion;
 
import cn.zzy.tm.model.db.PageBean;
 
/**
 * 增删改查通用接口 Dao层的接口可以继承本基本接口进行扩展
 * 
 * Hibernate使用小记 根据业务,常用的字段需要join的就fetch=join,不需要的就开lazy,fetch=select
 * 
 * DAO泛型模板类, 提供各类基础的查询模版方法,也提供懒加载的回调接口,方便初始化懒加载的数据
 * 
 * ,(解决代码分层session关闭后懒加载失效的问题)这个偶尔使用,一般很少使用这些回调的接口,
 * 
 * 因为特殊的查询全用HQL了,而HQL是可以指定特殊字段的抓取方式的。
 * 
 * 
 * 如果某个实体内的一些字段内,只有某些少数业务查询需要fetch=join,其他大部分都不需要这部分数
 * 
 * 据的时候,建议配置文件开lazy,fetch=select 查询全用HQL指定left join fetch来抓取指定字段
 * 
 * 因为HQL查询里配置的抓取方式全无效,默认全是lazy+select,需要在代码里指定 比方说 from Teacher as t left join
 * fetch t.teacherAcademicTitle
 * 
 * 
 * 
 * @author 赵泽洋
 * 
 * @param <T>
 *            要实现基本操作的bean的类
 */
public interface GenericDaoI<T extends Serializable> {
 
    /**
     * 回调接口,初始化代理对象用
     * 
     * @author zhaozeyang
     * 
     */
    public interface InitializeObjCallBack<T> {
        public void initializeObj(final T obj);
    }
 
    /**
     * 回调接口,初始化代理对象LIST用
     * 
     * @author zhaozeyang
     * 
     */
    public interface InitializeListCallBack<T> {
        public void initializeList(final List<T> ls);
    }
 
    /**
     * 新增一条数据
     * 
     * @param entity
     *            实体
     */
    public abstract void create(final T entity);
 
    /**
     * 新增一条数据 若存在则更新
     * 
     * @param entity
     */
    public abstract void createOrUpdate(final T entity);
 
    /**
     * 根据主键查实体
     * 
     * @param id
     *            主键
     * @return 查询结果
     */
    public abstract T findById(final Serializable id);
 
    /**
     * 根据主键查实体
     * 
     * @param id
     * @param callback
     *            回调接口
     * @return
     */
    public abstract T findById(final Serializable id,
            InitializeObjCallBack<T> callback);
 
    /**
     * 根据id删数据,可传多个id
     * 
     * @param entityids
     *            id序列
     */
    public abstract void deleteById(Serializable... entityids);
 
    /**
     * 更新实体类到数据库
     * 
     * @param entity
     *            要更新的实体类
     */
    public abstract void update(final T entity);
 
    /**
     * 查所有记录
     * 
     * @return 查询结果
     */
    public abstract List<T> findAll();
 
    /**
     * 查所有记录
     * 
     * @param callBack
     *            回调接口
     * @return
     */
    public abstract List<T> findAll(InitializeListCallBack<T> callBack);
 
    /**
     * HQL查记录
     * 
     * @param strHQL
     *            HQL语句
     * @param params
     *            参数
     * @return 查询结果
     */
    public abstract List<T> findByHQL(final String strHQL,
            final Object... params);
 
    /**
     * HQL查记录
     * 
     * @param callBack
     *            回调接口
     * @param strHQL
     *            HQL语句
     * @param params
     *            参数
     * @return 查询结果
     */
    public abstract List<T> findByHQL(InitializeListCallBack<T> callBack,
            final String strHQL, final Object... params);
    /**
     * HQL分页查找
     * 
     * @param strHQL
     *            HQL语句
     * @param currentPage
     *            查第几页
     * @param pageSize
     *            一个页面数据的条数
     * @param params
     *            查询结果
     * @return
     */
    public abstract PageBean<T> findByPage(final String strHQL,
            final int currentPage, final int pageSize, final Object... params);
 
    /**
     * HQL分页查找
     * 
     * @param callBack
     *            回调接口
     * @param strHQL
     * @param currentPage
     * @param pageSize
     * @param params
     * @return
     */
    public abstract PageBean<T> findByPage(InitializeListCallBack<T> callBack,
            final String strHQL, final int currentPage, final int pageSize,
            final Object... params);
 
    /**
     * 用Criteria方式查找数据
     * 
     * @param prams
     * @return
     */
    public List<T> findByCriteria(Criterion... prams);
 
    /**
     * 用Criteria方式查找数据
     * 
     * @param callBack
     *            回调接口
     * @param prams
     * @return
     */
    public List<T> findByCriteria(InitializeListCallBack<T> callBack,
            Criterion... prams);
 
    public Integer getCount();
 
    /**
     * 执行HQL delete update操作
     */
    public int executeHQL(final String strHQL, final Object... params);
 
    /**
     * 当返回只有一个实例的时候使用
     * 
     * @param strHQL
     * @param params
     * @return
     */
    public Object ExecuteScalarByHql(final String strHQL,
            final Object... params);
 
}
实现类GenericDaoImpl:
 
package cn.zzy.tm.dao.impl;
 
import java.io.Serializable;
import java.util.List;
 
import javax.annotation.Resource;
 
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Projections;
 
import cn.zzy.tm.dao.GenericDaoI;
import cn.zzy.tm.model.db.PageBean;
import cn.zzy.tm.utils.GenericsUtils;
 
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
 
/**
 * Dao层模版类
 * 
 * @author 赵泽洋
 * 
 * @param <T>
 *            要实现基本操作的bean的类
 * 
 */
@Transactional
@Repository
public class GenericDaoImpl<T extends Serializable> implements GenericDaoI<T> {
 
    /**
     * 获取实体类类型
     */
    @SuppressWarnings("unchecked")
    protected Class<T> persistentClass = GenericsUtils
            .getSuperClassGenricType(this.getClass());
 
    /**
     * 若反射获得泛型类类型失效,则由此方法手动设置
     * 
     * @param entityClass
     *            实体类类型
     */
    public void setEntityClass(Class<T> entityClass) {
        this.persistentClass = entityClass;
    }
 
    /**
     * 由spring管理的SessionFactory,注解获取
     */
    @Resource
    protected SessionFactory sessionFactory;
 
    public void create(T entity) {
        sessionFactory.getCurrentSession().persist(entity);
    }
 
    public void createOrUpdate(T entity) {
        sessionFactory.getCurrentSession().saveOrUpdate(entity);
    }
 
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public T findById(Serializable id) {
        return this.findById(id, null);
    }
 
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public T findById(Serializable id, InitializeObjCallBack<T> callback) {
        if (id == null)
            throw new RuntimeException(this.persistentClass.getName()
                    + ":传入的实体id不能为空");
        Object object = sessionFactory.getCurrentSession().get(
                this.persistentClass, id);
        if (callback != null)
            callback.initializeObj((T) object);
        return (T) object;
    }
 
    public void deleteById(Serializable... entityids) {
        Session session = sessionFactory.getCurrentSession();
        for (Serializable id : entityids) {
            session.delete(session.get(this.persistentClass, id));
        }
 
    }
 
    public void update(T entity) {
        sessionFactory.getCurrentSession().merge(entity);
    }
 
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public List<T> findAll() {
        return this.findByCriteria();
    }
 
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public List<T> findAll(InitializeListCallBack<T> callBack) {
        return this.findByCriteria(callBack);
    }
 
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public List<T> findByHQL(String strHQL, Object... params) {
 
        return this.findByHQL(null, strHQL, params);
    }
 
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public List<T> findByHQL(InitializeListCallBack<T> callBack, String strHQL,
            Object... params) {
        Session session = sessionFactory.getCurrentSession();
        Query query = session.createQuery(strHQL);
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i, params[i]);
        }
        List<T> ls = query.list();
        if (callBack != null)
            callBack.initializeList(ls);
        return ls;
    }
 
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public PageBean<T> findByPage(String strHQL, int currentPage, int pageSize,
            Object... params) {
        return this.findByPage(null, strHQL, currentPage, pageSize, params);
 
    }
 
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public PageBean<T> findByPage(InitializeListCallBack<T> callBack,
            String strHQL, int currentPage, int pageSize, Object... params) {
        // 步骤1:创建一个PageBean对象
        PageBean<T> pageBean = new PageBean<T>();
        // 步骤2:获取一个数据库链接session
        Session session = sessionFactory.getCurrentSession();
        // 步骤3:执行HQL语句完成查询动获取本页内的固定条数的数据
        Query query = session.createQuery(strHQL);
        // 步骤4:设置查询条件-参数条件
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i, params[i]);
        }
        // 步骤5:设置查询条件-每页的启始记录下标 (当前也是-1)*每页个数
        query.setFirstResult((currentPage - 1) * pageSize);
        // 步骤6:设置查询条件-控制查询记录的个数
        query.setMaxResults(pageSize);
        // 步骤7:获取数据集合并且赋值给pageBean对象的data属性
        pageBean.setData(query.list());
 
        // 步骤8:将输入的HQL语句动态查分成符合返回记录个数的HQL语句
        strHQL = "select count(*) "
                + strHQL.substring(strHQL.toLowerCase().indexOf("from"));
        //如果自定义了fetch,必须去掉fetch才能查出count(*)
        strHQL = strHQL.replace("fetch", "");
        // 步骤9:执行HQL语句完成查询动获取本页内的固定条数的数据
        query = session.createQuery(strHQL);
        // 步骤10:设置查询条件-参数条件
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i, params[i]);
        }
        // 步骤11:获取查询结果并且赋值给pageBean对象的totalRows
        pageBean.setTotalRows(Integer.parseInt(query.uniqueResult().toString()));
        // 步骤12:为剩余的pageBean属性赋值
        pageBean.setCurrentPage(currentPage);
        pageBean.setPageSize(pageSize);
        if (callBack != null)
            callBack.initializeList(pageBean.getData());
        return pageBean;
    }
 
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public List<T> findByCriteria(Criterion... prams) {
        return this.findByCriteria(null, prams);
    }
 
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public List<T> findByCriteria(InitializeListCallBack<T> callBack,
            Criterion... prams) {
        Session session = sessionFactory.getCurrentSession();
        Criteria criteria = session.createCriteria(this.persistentClass);
        for (Criterion criterion : prams) {
            criteria.add(criterion);
        }
        List<T> ls = criteria.list();
        if (callBack != null)
            callBack.initializeList(ls);
        return ls;
 
    }
 
    public Integer getCount() {
        Session session = sessionFactory.getCurrentSession();
        Criteria c = session.createCriteria(this.persistentClass);
        c.setProjection(Projections.rowCount());
        return  (Integer) c.uniqueResult();
 
    }
 
    public int executeHQL(String strHQL, Object... params) {
 
        Session session = sessionFactory.getCurrentSession();
        Query query = session.createQuery(strHQL);
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i, params[i]);
        }
        return query.executeUpdate(); 
    }
 
    public Object ExecuteScalarByHql(String strHQL, Object... params) {
        Session session = sessionFactory.getCurrentSession();
        Query query = session.createQuery(strHQL);
        for (int i = 0; i < params.length; i++) {
            query.setParameter(i, params[i]);
        }
        return query.uniqueResult();
    }
 
 
}
具体实现随便举个例子: 
具体业务接口AdminBizI :
 
package cn.zzy.tm.logic;
 
import cn.zzy.tm.dao.GenericDaoI;
import cn.zzy.tm.model.db.Admin;
 
public interface AdminBizI extends GenericDaoI<Admin> {
 
    /**
     * 获取能用的管理员数量,没被禁用的
     * @return
     */
    public Long getUseableAdmin();
}
具体业务接口的实现类:
 
package cn.zzy.tm.logic.impl;
 
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import cn.zzy.tm.dao.impl.GenericDaoImpl;
import cn.zzy.tm.logic.AdminBizI;
import cn.zzy.tm.model.db.Admin;
 
@Service
@Transactional
public class AdminBizImpl extends GenericDaoImpl<Admin> implements AdminBizI {
 
    public Long getUseableAdmin() {
        return (Long) this
                .ExecuteScalarByHql("select count(*) from Admin as t where t.adminSuspended=0");
 
    }
}
接下来是关于代码分层导致不能取得懒加载配置的数据然后hibernate session关闭的问题,解决方法有很多,但是不推荐使用spring中扩大session生命周期的方法,如果执行时间过长或网络堵塞会导致此session迟迟得不到释放,并发大的时候肯定挂了。
 
我的解决方法就如上模板类的代码所示,回调接口, 
在回调接口里把需要的数据load进来就好。 
或关联查询的时候用HQL自定义抓取方式,只抓取需要的
 
回调接口使用举例:
 
studentTask = studentTaskBizImpl.findById(sessionStu.getStudentId(),
                new InitializeObjCallBack<StudentTask>() {
 
                    public void initializeObj(StudentTask obj) {
                        if (obj != null) {
                            Hibernate.initialize(obj.getTask());
                            Hibernate.initialize(obj.getTask().getTeacher());
                        }
 
                    }
                });
自定义抓取方式举例
 
pgb = taskBizImpl
                        .findByPage(
                                "from Task as t "
                                        + "left join fetch t.tmtype left join fetch t.dicByTaskWhereToDo left join fetch t.dicByTaskState left join fetch t.dicByTaskType left join fetch t.teacher  "
                                        + "where t.dicByTaskState.dicId=?  order by t.tmtype.tmtypeId, t.taskCreateTime ",
                                page, rows, "200");
当然更复杂的直接用sql,可以用hibernate里的sql查询方式,自定义结果集的bean,或者spring里的JdbcTemplate。
 
当然涉及到项目数据量很大,肯定是不使用关系,也就不会使用hibernate,一般都把复杂业务写成存储过程,用mybatis方便sql的编写执行。