Hibernate 的应用7 —— 与 Spring 的整合

Spring 的 DAO 支持

Spring 提供许多工具类用于对 DAO 实现简化开发步骤

Spring 提供了一致的异常抽象,将原有的 Checked 异常转换包装为 Runtime 异常,因而,编码时无序捕获各种技术中特定的异常

  • HibernateTemplate

    public class PersonDaoImpl implements PersonDao {
    
        private HibernateTemplate ht = null;
    
        @Resource(name="sessionFactory")
        protected SessionFactory sessionFactory;
        
        public SessionFactory getSessionFactory() {
            return sessionFactory;
        }
        
        public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        } 
        
        private HibernateTemplate getHibernateTemplate() {
            if (ht == null) {
                ht = new HibernateTemplate(sessionFactory);
            }
            return ht;
        }
        
        // HibernateTemplate 的使用类似于 Session + Query
        public Person get(Integer id) {
            return getHibernateTemplate().get(Person.class, id);
        }
    }
    

    缺点:灵活性不足,不能用 Hibernate API 进行持久化访问

  • HibernateCallback 接口

    • HibernateTemplate 的 .execute(HibernateCallback action).executeFind(HibernateCallback action) 方法接收一个 HibernateCallback 接口的实现
    • HibernateCallback 接口包含 doInHibernate(Session session) 方法
    • 两者结合可解决灵活应用 Hibernate API 的问题
    public class PersonDaoImpl implements PersonDao {
    
        private HibernateTemplate ht = null;
    
        @Resource(name="sessionFactory")
        protected SessionFactory sessionFactory;
        
        public SessionFactory getSessionFactory() {
            return sessionFactory;
        }
        
        public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        } 
        
        private HibernateTemplate getHibernateTemplate() {
            if (ht == null) {
                ht = new HibernateTemplate(sessionFactory);
            }
            return ht;
        }
        
        public Person get(final Integer id) {
            return getHibernateTemplate().execute(new HibernateCallback() {
                public Object doInHibernate(Session session) throws HibernateException, SQLException {
                    return session.get(Person.class, id);
                }
            });
        }
    }
    
  • HibernateDaoSupport

    • HibernateDaoSupport 的 getHibernateTemplate() 方法返回 HibernateTemplate 对象
    • HibernateDaoSupport 的 setSessionFactory(SessionFactory sessionFactory) 可用于接收 Spring 的依赖注入,从而允许使用 sessionFactory 对象
    • HibernateDaoSupport 的 DAO 实现由 HibernateTemplate 对象完成
    • DAO 类的实现继承 HibernateDaoSupport

Spring 使用声明式事务

  • Spring 使用声明式事务来进行统一的事务管理
  • 只需要在配置文件中增加事务控制片段,业务逻辑组件的方法将会有事务性
  • Spring 的声明式事务支持在不同事务策略之间自由切换

配置 applicationContext.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd>

    <description>Spring公共配置</description>
    
    <!-- 自动扫描与装配bean -->
    <context:component-scan base-package="com.lian" />
    
    <!-- 导入外部的 properties 文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 配置 dataSource 和 sessionFactory -->
    
    <!-- 配置事务 -->
    
</beans>

配置 dataSource 和 sessionFactory

  • 直接在 spring 的配置文件 applicationContext.xml 中配置
    <!-- 配置c3p0数据库连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 数据连接信息 -->
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <!-- 其他配置 -->
        <!--初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
        <property name="initialPoolSize" value="3"></property>
        <!--连接池中保留的最小连接数。Default: 3 -->
        <property name="minPoolSize" value="3"></property>
        <!--连接池中保留的最大连接数。Default: 15 -->
        <property name="maxPoolSize" value="5"></property>
        <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
        <property name="acquireIncrement" value="3"></property>
        <!-- 控制数据源内加载的PreparedStatements数量。如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0 -->
        <property name="maxStatements" value="8"></property>
        <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
        <property name="maxStatementsPerConnection" value="5"></property>
        <!--最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
        <property name="maxIdleTime" value="1800"></property>
    </bean>
    
    <!-- 配置 sessionFactory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <!-- 设置 dataSource 属性 -->
        <property name="dataSource" ref="dataSource"></property>
        <!-- 配置 Hibernate Properties -->
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hbm2ddl.auto">update</prop>
            </props>
        </property>
        <!-- 配置 Mapping Resources -->
        <property name="mappingLocations" value="classpath:com/lian/entity/*.hbm.xml"></property>
    </bean>
    
  • 引用 Hibernate 的配置文件 hibernate.cfg.xml
    <!-- 配置 sessionFactory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <!-- 引用 Hibernate 的配置文件来配置 dataSource 和 Hibernate Properties -->
        <property name="configLocation" value="classpath:hibernate.cfg.xml" />
        <!-- 配置 Mapping Resources -->
        <property name="mappingLocations" value="classpath:com/lian/entity/*.hbm.xml"></property>
    </bean>
    

当相同的配置项都存在时,applicationContext.xml 中的优先级高于 hibernate.cfg.xml

配置事务

  • XML 配置

    <!-- 配置 局部事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    
    <!-- 配置 增强事务 Bean -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 配置 详细的事务语义 -->
        <tx:attributes>
            <!-- 为不同的方法指定相应的事务语义 -->
            <!-- 所有以 get 开头的方法都是 read-only 的 -->
            <tx:method name="get*" read-only="true"/>
            <!-- 其他方法使用默认的事务设置 -->
            <tx:method name="*">
        </tx:attributes>
    </tx:advice>
    
    <aop:config>
        <!-- 配置一个切入点 -->
        <aop:pointcut id="onePointcut" expression="bean(personService)"/>
        <!-- 指定在 onePointcut 切入点应用 txAdvice 事务增强处理 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="onePointcut"/>
    </aop:config>
    
  • Annotation

    <!-- 配置 局部事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    
    <!-- 根据 Annotation 生成事务代理 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    • @Transactional(...)

      属性 说明
      name 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器
      propagation 事务的传播行为,默认值为 REQUIRED
      isolation 事务的隔离度,默认值采用 DEFAULT
      timeout 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务
      read-only 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true
      rollback-for 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔
      no-rollback- for 抛出 no-rollback-for 指定的异常类型,不回滚事务

透彻的掌握 Spring 中@transactional 的使用

Spring @Transactional工作原理

Spring @Transactional原理及使用

Hibernate 框架下的通用 DAO 层

BaseDao.java

/**
 * 通用泛型DAO
 */
public interface BaseDao<T> {

    /**
     * 新增一个实例
     * @param entity 要新增的实例 
     */
    public void save(T entity);
    
    /**
     * 根据主键删除一个实例 
     * @param id 主键
     */
    public void delete(int id);
    
    /**
     * 编辑指定实例的详细信息
     * @param entity 实例 
     */
    public void edit(T entity);
    
    /**
     * 根据主键获取对应的实例 
     * @param id 主键值
     * @return 如果查询成功,返回符合条件的实例;如果查询失败,返回null
     */
    public T get(Integer id);
    
    /**
     * 根据主键获取对应的实例 
     * @param id 主键值
     * @return 如果查询成功,返回符合条件的实例;如果查询失败,抛出空指针异常
     */
    public T load(Integer id);
    
    /**
     * 获取所有实体实例列表
     * @return 符合条件的实例列表
     */
    public List<T> findAll();
    
    /**
     * 统计总实体实例的数量
     * @return 总数量
     */
    public int totalCount();
    
    /**
     * 获取分页列表
     * @param pageNo 当前页号
     * @param pageSize 每页要显示的记录数
     * @return 符合分页条件的分页模型实例
     */
    public PageModel<T> findByPager(int pageNo, int pageSize);
    
    /**
     * 根据指定的SQL语句和参数值执行更新数据的操作
     * @param sql SQL语句
     * @param paramValues 参数值数组
     */
    public void update(String sql);
    
    /**
     * 根据指定的SQL语句和参数值执行单个对象的查询操作
     * @param sql SQL语句
     * @param paramValues 参数值
     * @return 符合条件的实体对象
     */
    public T findUnique(String sql);
}

BaseDaoImpl.java

/**
 * 通用DAO接口的实现类
 */
@SuppressWarnings("unchecked")
public class BaseDaoImpl<T> implements BaseDao<T> {

    /**
     * 对应的持久化类
     */
    private Class<T> clazz;
    
    @Resource(name="sessionFactory")
    protected SessionFactory sessionFactory;
    
    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    
    public BaseDaoImpl(){
        //通过反射机制获取子类传递过来的实体类的类型信息
        ParameterizedType type=(ParameterizedType)this.getClass().getGenericSuperclass();
        this.clazz=(Class<T>)type.getActualTypeArguments()[0];
    }

    @Override
    public void save(T entity) {
        Session session = sessionFactory.getCurrentSession();
        session.save(entity);
    }

    @Override
    public void delete(int id) {
        Session session = sessionFactory.getCurrentSession();
        session.delete(get(id));
    }

    @Override
    public void edit(T entity) {
        Session session = sessionFactory.getCurrentSession();
        session.merge(entity);
    }

    @Override
    public T get(Integer id) {
        Session session = sessionFactory.getCurrentSession();
        return (T) session.get(clazz, id);
    }

    @Override
    public T load(Integer id) {
        Session session = sessionFactory.getCurrentSession();
        return (T)session.load(clazz, id);
    }

    @Override
    public List<T> findAll() {
        Session session = sessionFactory.getCurrentSession();
        String hql = "select t from "+clazz.getSimpleName()+" t";
        return (List<T>)session.createQuery(hql).list();
    }

    @Override
    public int totalCount() {
        Session session = sessionFactory.getCurrentSession();
        int count = 0;
        String hql = "select count(t) from "+clazz.getSimpleName()+" t";
        Long temp = (Long)session.createQuery(hql).uniqueResult();
        if(temp != null){
            count = temp.intValue();
        }
        return count;
    }

    @Override
    public PageModel<T> findByPager(int pageNo, int pageSize) {
        Session session = sessionFactory.getCurrentSession();
        PageModel<T> pm = new PageModel<T>(pageNo, pageSize);
        String hql = "select t from "+clazz.getSimpleName()+" t";
        int startRow = (pageNo - 1) * pageSize;
        pm.setDatas(session.createQuery(hql).setFirstResult(startRow).setMaxResults(pageSize).list());
        pm.setRecordCount(totalCount());
        return pm;
    }

    @Override
    public void update(String hql) {
        Session session = sessionFactory.getCurrentSession();
        session.createQuery(hql);
    }

    @Override
    public T findUnique(String hql) {
        Session session = sessionFactory.getCurrentSession();
        return (T)session.createQuery(hql).uniqueResult();
    }
}

PageModel.java

/**
 * 分页模型类
 */
public class PageModel<T> {

    //当前页号
    private int pageNo=1;
    //每页显示的记录数
    private int pageSize=10;
    //总记录数
    private int recordCount;
    //总页数
    private int pageCount;
    //存放分页数据的集合
    private List<T> datas;
    
    public PageModel(){
        
    }
    
    public PageModel(int pageNo,int pageSize){
        this.pageNo=pageNo;
        this.pageSize=pageSize;
    }

    public int getPageNo() {
        return pageNo;
    }

    public void setPageNo(int pageNo) {
        this.pageNo = pageNo;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getRecordCount() {
        return recordCount;
    }

    public void setRecordCount(int recordCount) {
        this.recordCount = recordCount;
    }

    public int getPageCount() {
        if(this.getRecordCount()<=0){
            return 0;
        }else{
            pageCount=(recordCount+pageSize-1)/pageSize;
        }
        return pageCount;
    }

    public void setPageCount(int pageCount) {
        this.pageCount = pageCount;
    }

    public List<T> getDatas() {
        return datas;
    }

    public void setDatas(List<T> datas) {
        this.datas = datas;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,869评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,716评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,223评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,047评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,089评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,839评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,516评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,410评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,920评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,052评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,179评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,868评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,522评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,070评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,186评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,487评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,162评论 2 356

推荐阅读更多精彩内容