Spring与Hibernate3集成

2022-11-15 13:37:09 浏览数 (1)

1 使用Spring提供的模板简化DAO开发

        Spring内置了一组DAO组件,可以针对JDBC、Hibernate、iBATIS等常见数据访问技术提供简化操作,让我们把精力集中在核心的数据操作上。

(1)DAO模板

        我们日常编写的数据访问代码中,大部分都是管道代码(重复不变的),只要少数几句核心代码是每个方法不相同的。Spring利用“模板方法”设计模式,把管道代码预先定义好,然后通过委托的方式,把方法的特定细节(变化部份)委托到外部交给程序员去实现,基本上消除了数据访问的冗余代码。

      所谓“模板方法”设计模式,GoF的定义是:在一个方法里定义算法的骨架,将一些步骤延迟到其子类。下图显示,Spring的DAO模板(DAO Template)中定义了公共的DAO管道代码(如连接的开关和事务的开关),对于特定任务(如执行不同的SQL语句)则调用自定义DAO的回调对象(Java中的委托使用接口来实现)。

1.1 Spring的HibernateTemplate

        Spring针对Hibernate提供了如下所示模板方法类,用于简化Hibernate操作。

        org.springframework.orm.hibernate3.HibernateTemplate

        下表为hibernateTemplate中的常用方法。

方法

描述

T get(Class<T> entityClass, Serializable id)

根据id查询单个持久化对象

Serializable save(final Object entity)

保存(添加)实体对象并返回id

void update(Object entity)

更新实体对象

void delete(Object entity)

删除持久化对象

List<?> find(String hql, Object... values)

使用hql和顺序参数(对象数组,Object[])values查询持久化对象,并返回List集合

List<?> findByNamedParam(String hql, String[] paramNames, Object[] values)

使用hql和命名参数(名数组和值数组)查询持久化对象,并返回List集合

List<?> findByCriteria(DetachedCriteria criteria, int firstResult, int maxResults)

使用DetachedCriteria查询持久化对象,可以传入控制返回记录数(分页)的起始行号和最大返回行数

T execute(HibernateCallback<T> action)

使用回调HibernateCallback接口控制session执行的全过程,整个执行过程由委托出来的实现类控制,适用于各种复杂场景

int bulkUpdate(String hql, Object... values)

使用hql实现批处理(批量删除或更新)

        通过下面示例可以看到,使用了HibernateTemplate类的“模板方法”简化后的DAO开发,我们无需关心Session如何获取及其Transaction如何提交,只需要编写核心的数据访问代码即可(Session的打开关闭和事务处理等管道代码都由模板本身提供了)。

代码语言:javascript复制
        public void updateUser(User user) {

                 hibernateTemplate().update(user);

        }

        public User fetchById(int id) {

                 return hibernateTemplate().get(User.class, id);

        }

        public User fetchUserByUsername(String username){

                 List<User> list = (List<User>)hibernateTemplate()

                                                  .find("from User u where u.username=?", username);

                 if(list.size()==0)

                         return null;

                 else

                         return list.get(0);

        }

        需要注意的是HibernateTemplate内部是依赖于Session的,因此需要为它注入SessionFactory对象。

        在Spring整合Hibernate3开发时,我们可以通过两种方式来获得HibernateTemplate的支持。

(1)在DAO实现类中声明hibernateTemplate属性

        定义DAO:

代码语言:javascript复制
public class CategoryDaoImpl implements CategoryDao {

        private HibernateTemplate hibernateTemplate;

        public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {

                 this.hibernateTemplate = hibernateTemplate;

        }

        public Category fetchById(int id){

                 return hibernateTemplate.get(Category.class, id);

        }

        ……

}

        实现对DAO类和HibernateTemplate类的依赖注入

代码语言:javascript复制
        <bean id="hibernateTemplate"

                         class="org.springframework.orm.hibernate3.HibernateTemplate">

                 <property name="sessionFactory" ref="sessionFactory"/>

        </bean>

        <bean id="categoryDao" class="mycinema.dao.impl.CategoryDaoImpl">

                 <property name="hibernateTemplate" ref="hibernateTemplate" />

        </bean>

(2)继承HibernateDaoSupport基类,使用该基类内置的hibernateTemplate属性

代码语言:javascript复制
public class MovieDaoImpl extends HibernateDaoSupport implements MovieDao {

        public List<Movie> getAll() {

                 return (List<Movie>)getHibernateTemplate().find("from Movie");

        }

        ……

}

        实现对DAO类(继承自基类)的sessionFactory属性的依赖注入

代码语言:javascript复制
        <bean id="movieDao" class="mycinema.dao.impl.MovieDaoImpl">

                 <property name="sessionFactory" ref="sessionFactory" />

        </bean>

1.2 Spring整合Hibernate3的实现步骤

(1)添加相关依赖

        以下示例使用了如下依赖:Hibernate3.6、MySQL驱动、DBCP数据源、Spring DI和Spring ORM。其中DBCP数据源和SpringORM为新增依赖。

代码语言:javascript复制
                 <!-- JDBC 驱动,必须 -->

                 <dependency>

                         <groupId>mysql</groupId>

                         <artifactId>mysql-connector-java</artifactId>

                         <version>5.1.8</version>

                 </dependency>

                 <!-- dbcp 数据源(连接池),必须 -->

                 <dependency>

                         <groupId>commons-dbcp</groupId>

                         <artifactId>commons-dbcp</artifactId>

                         <version>1.2.2</version>

                 </dependency>

                 <!-- Hibernate 3.6.10, Core Annotation,必须 -->

                 <dependency>

                         <groupId>org.hibernate</groupId>

                         <artifactId>hibernate-core</artifactId>

                         <version>3.6.10.Final</version>

                 </dependency>

                 <dependency>

                         <groupId>javassist</groupId>

                         <artifactId>javassist</artifactId>

                         <version>3.9.0.GA</version>

                 </dependency>

                 <!-- Spring DI容器 -->

                 <dependency>

                         <groupId>org.springframework</groupId>

                         <artifactId>spring-context</artifactId>

                         <version>4.2.5.RELEASE</version>

                 </dependency>

                 <!-- Spring ORM -->

                 <dependency>

                         <groupId>org.springframework</groupId>

                         <artifactId>spring-orm</artifactId>

                         <version>4.2.5.RELEASE</version>

                 </dependency>

(2)在Spring中配置SessionFactory

        此前,我们使用hibernate.cfg.xml配置SessionFactory,现在要用Spring整合,所有功能bean都应由Spring提供,包括Hibernate的Session,因此SessionFactory也应该配置在Spring的applicationContext.xml中,而hibernate.cfg.xml则可以去掉。

        为了加强数据库连接的管理,我们还应该配置数据源(DataSource),使用数据源和连接池提供连接对象给SessionFactory,这里使用DBCP作为数据源。

代码语言:javascript复制
​
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

     http://www.springframework.org/schema/beans

     http://www.springframework.org/schema/beans/spring-beans.xsd">

        <!-- 配置数据源 -->

        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"

                 destroy-method="close">

               <property name="driverClassName" value="com.mysql.jdbc.Driver" />

                 <property name="url" value="jdbc:mysql://localhost:3306/MyCinema" />

                 <property name="username" value="root" />

                 <property name="password" value="1234" />

                 <!-- 初始化连接大小 -->

                 <property name="initialSize" value="0"></property>

                 <!-- 连接池最大数量 -->

                 <property name="maxActive" value="20"></property>

                 <!-- 连接池最大空闲 -->

                 <property name="maxIdle" value="20"></property>

                 <!-- 连接池最小空闲 -->

                 <property name="minIdle" value="1"></property>

                 <!-- 获取连接最大等待时间 -->

                 <property name="maxWait" value="60000"></property>

        </bean>

<!-- 配置SessionFactory -->

        <bean id="sessionFactory"

                class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

                 <property name="dataSource" ref="dataSource" />

                 <property name="mappingResources">

                         <list>

                                  <value>mappings/User.hbm.xml</value>

                                  ……

                         </list>

                 </property>

                 <property name="hibernateProperties">

                         <props>

                                  <prop key="hiberante.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>

                                  <prop key="hibernate.show_sql">true</prop>

                         </props>

                 </property>

        </bean>

</beans>

​

(3)使用HibernateTemplate实现DAO功能

        有两种方式可以使用HibernateTemplate:一、直接在DAO中包含一个HibernateTemplate对象,然后用Spring依赖注入;二、继承HibernateDaoSupport,父类中已经包含了HibernateTemplate,为DAO注入SessionFactory。

        方式一:

        代码部分,

代码语言:javascript复制
public class UserDaoImpl implements UserDao {

        private HibernateTemplate hibernateTemplate;

        public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {

                 this.hibernateTemplate = hibernateTemplate;

        }

        public User checkLogin(String username, String password) {

                 List<User> list= (List<User>)getHibernateTemplate()

.find("from User u where u.username=? and u.password=?", username, password);

                 return list.size()==0?null:list.get(0);

        }

}

        配置部分。

代码语言:javascript复制
<!-- 配置hibernateTemplate对象 -->

<bean id="hibernateTemplate"

class="org.springframework.orm.hibernate3.HibernateTemplate">

        <property name="sessionFactory" ref="sessionFactory" />

</bean>

<!-- 将hibernateTemplate注入DAO类对象 -->

        <bean id="userDao" class="mycinema.dao.impl.UserDaoImpl">

                 <property name="hibernateTemplate" ref="hibernateTemplate" />

        </bean>

        ……

        方式二:

        代码部分,

代码语言:javascript复制
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

        public User checkLogin(String username, String password) {

                 List<User> list= (List<User>)getHibernateTemplate()

.find("from User u where u.username=? and u.password=?", username, password);

                 return list.size()==0?null:list.get(0);

        }

}

        配置部分。

代码语言:javascript复制
<!-- 将sessionFactory注入继承自HibenrateDaoSupport的DAO类对象 -->

        <bean id="userDao" class="mycinema.dao.impl.UserDaoImpl">

                 <property name="sessionFactory" ref="sessionFactory " />

        </bean>

1.3 使用Callback实现复杂的DAO操作

        如果所需操作比较复杂,无法通过简单的模板方法调用来实现(如使用HQL分页或更复杂的查询),HibernateTemplate还提供了execute()方法,提供HibernateCallback类型的回调(委托)对象作为参数,让外界全程控制数据操作过程(完全控制Session和查询过程)。

T HibernateTemplate.execute (HibernateCallback<T> callback);

        使用值得注意的是HibernateCallback参数:

(1)HibernateCallback是一个接口,该接口只有一个方法, doInHibernate (session),该方法的参数正是数据操作所需的Hibernate的Session。

  1. 方法 doInHibernate 的方法体就是Spring执行的Hibernate数据访问操作。

(3)使用HibernateTemplate执行execute (new HibernateCallback())方法,从doInHibernate得到session,并用session完成所需的数据访问操作。

(4)HibernateCallback回调对象实际就是一种事件委托模式,给使用者预留下了全程编码控制数据访问的位置。

        下面是唱片标题的自动完成下拉提示的数据查询方法,其中使用了executeFind():

代码语言:javascript复制
        public List<String> getAlbumTitlesByPrefix(final String prefix, final int count) {

                 List<String> list = getHibernateTemplate().executeFind(new HibernateCallback<List>() {

                         public List doInHibernate(Session sess){

                                  String hql = "select a.title from Album a where a.title like :title";

                                  return sess.createQuery(hql).setString("title", prefix "%")

                                  .setMaxResults(count).list();

                         }

                 });

                 return list;

        }

2 使用Spring的声明式事务管理

        Spring利用AOP切面技术,为数据访问提供了基于业务层(一个业务方法往往代表一个事务,可以包含多个DAO方法)的声明式事务管理,完全透明地解决了事务难题。所谓声明式的事务管理:即只需配置,无须编程,利用AOP技术,把事务代码横切织入到数据访问代码中。

        Spring针对不同的数据访问方式,提供了不同的事务管理器,如下所示:

2.1 使用Hibernate3的事务管理器

        这里讨论的是Hibernate3的事务管理器:orm.hibernate3.HibernateTransactionManager。

(1)导入所需要依赖。

        这里需要用到AOP和切面描述,因此需要在原来基础上添加Spring的切面依赖。

代码语言:javascript复制
<!-- Spring 切面,可用于配置事务切面 -->

                 <dependency>

                         <groupId>org.springframework</groupId>

                         <artifactId>spring-aspects</artifactId>

                         <version>${springframework.version}</version>

                 </dependency>

(2)在Spring配置文件的文档声明中加入aop和tx(事务)配置声明。

代码语言:javascript复制
​
<beans xmlns="http://www.springframework.org/schema/beans"

     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     xmlns:aop="http://www.springframework.org/schema/aop"

     xmlns:tx="http://www.springframework.org/schema/tx"

     xsi:schemaLocation="

     http://www.springframework.org/schema/beans

     http://www.springframework.org/schema/beans/spring-beans.xsd

     http://www.springframework.org/schema/tx

     http://www.springframework.org/schema/tx/spring-tx.xsd

     http://www.springframework.org/schema/aop

     http://www.springframework.org/schema/aop/spring-aop.xsd">

        ……

</beans>

​

(3)配置Hibernate事务管理器。

代码语言:javascript复制
    <bean id="transactionManager"

                       class="org.springframework.orm.hibernate3.HibernateTransactionManager">

            <property name="sessionFactory" ref="sessionFactory" />

    </bean>

(4)配置AOP事务通知。                                                                 

代码语言:javascript复制
    <tx:advice id="txAdvice" transaction-manager="transactionManager">

         <tx:attributes>

             <tx:method name="get*" read-only="true" timeout="60"/>

             <tx:method name="fetch*" read-only="true" timeout="60"/>

             <tx:method name="add*" propagation="REQUIRED" timeout="60"/>

             <tx:method name="update*" propagation="REQUIRED" timeout="60"/>

             <tx:method name="delete*" propagation="REQUIRED" timeout="60"/>

             <tx:method name="register" propagation="REQUIRED" timeout="60"/>

         </tx:attributes>

    </tx:advice>

(5)配置AOP切面(通知 切入点)。

代码语言:javascript复制
    <aop:config>

        <aop:advisor

            pointcut="execution(* mycinema.biz..*.*(..))"

            advice-ref="txAdvice"/>

        </aop:config>

3 Spring整合Hibernate并使用注解配置

3.1 Hibernate实体注解配置

(1)持久化实体注解

Hibernate的注解配置其实是Java EE 官方JPA规范(在SUN制定EJB3.0的JPA规范时,Hibernate的作者受邀成为主要起草者)的一个实现;因此,我们下面看到的注解均来自于Java EE的官方包“javax. persistence.*”(ejb3-persistence.jar)。

注解

描述

@Entity

用于标注该类型是持久化类

@Table

用于标注该持久化类所映射的数据库表

@Id

用于标注该属性是持久化对象的主键属性

@GeneratedValue

用于描述主键生成方式(主键值生成器,默认为auto)

@SequenceGenerator

用于描述主键生成器的序列(Oracle中的Sequence)信息

@Column

用于标注该对象属性所映射的数据库表的字段信息

@ManyToOne

用于标注该属性是多对一映射属性

@OneToOne

用于标注该属性是一对一映射属性

@OneToMany

用于标注该属性是一对多映射属性

@JoinColumn

用于描述连接字段(外键字段)信息

@Transient

用于标记某一个属性不需要要持久化

(2)配置实体映射

        示例:Category对象(One)的映射配置

代码语言:javascript复制
import javax.persistence.*;

@Entity

@Table(name="Category")

public class Category {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;
    @Column(name="Name")
    private String name;
    @OneToMany(mappedBy="category")
    private Set<Movie> movies = new HashSet<Movie>();
    //…省略属性getter/setter…

}

示例:Movie对象的映射配置。

代码语言:javascript复制
import java.sql.Date;

import javax.persistence.*;

@Entity
public class Movie {
  @Id
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  private int id;
  @Column
  private String movieCode;
  @Column
  private String title;
  @ManyToOne
  @JoinColumn(name="CategoryId")
  private Category category;
  @Column
  private String director;
  @Column
  private Date dateReleased;
  //…省略属性getter/setter…
}

3.2 Spring整合Hibernate注解配置

        如果Hibernate需要使用注解配置则,需要在SessionFactory配置中做以下修改。请注意红色字体部分。

代码语言:javascript复制
        <!-- SessionFactory -->

        <bean id="sessionFactory"

          class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

                 <property name="dataSource" ref="dataSource" />

                 <property name="annotatedClasses">

                         <list>

                                  <value>mycinema.entity.Category</value>

                                  <value>mycinema.entity.Movie</value>       

                         </list>

                 </property>

                 <property name="hibernateProperties">

                         <props>

                            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>

                            <prop key="hibernate.show_sql">true</prop>

                         </props>

                 </property>

        </bean>

        <!-- Hibernate Template -->

<bean id="hibernateTemplate"

class="org.springframework.orm.hibernate3.HibernateTemplate">

                 <property name="sessionFactory" ref="sessionFactory"/>

        </bean>

        <!-- 注解扫描范围 -->

        <context:component-scan base-package="mycinema" />

</beans>

3.3 Open Session In View模式

        Hibernate中虽然提供了Lazy load 延迟加载机制,但因为延时加载须要保证Session在不关闭的情况下才能进行,而我们往往在数据库事务结束时就已经吧Session关掉了,所以界面无法获得延时加载的外键属性。

        Spring的orm包中包含了一个可以实现OpenSessionInView功能的过滤器,可以实现在界面层延时加载Hibernate实体中的外键属性。所谓OpenSessionInView,就是确保在用户请求(request)开始时打开Hibernate Session,直到请求结束返回了视图结果后Session才关闭,在此之间Session一直开着,并由该同一个Session完成请求中所有的数据操作。

        在Spring Hibernate整合中,使用OpenSessionInView,只要在web.xml中配置以下过滤器即可(红字部份)。

<!--过滤器openSessionInViewFilter应位于struts2过滤器之前, 该过滤器类位于spring-orm包-->

代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>classpath:spring-beans.xml</param-value>

    </context-param>

    <listener>

       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

    <filter>

        <filter-name>openSessionInViewFilter</filter-name>

                 <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name>openSessionInViewFilter</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping>

</web-app>

        OpenSessionInView模式虽然可以发挥Hibernate中的延时加载特性,但也会带来另一个问题,就是Session打开的时间变长了,延长了Connection被占用的时间,这会对数据库性能有一些影响,是否应该使用须要具体问题具体分析;另外,Hibernate的延时加载策略也有其优缺点,也须要对症下药。

0 人点赞