文章目录
  1. 1. Spring事务管理
    1. 1.1. 事务管理 API
    2. 1.2. PlatformTransactionManager
  2. 2. 事务的隔离级别
  3. 3. 事务的传播行为
  4. 4. 事务管理方式
    1. 4.1. 实际案例: 转账
      1. 4.1.1. 声明式事务管理
        1. 4.1.1.1. TransactionProxyFactoryBean
        2. 4.1.1.2. tx, 自动代理
        3. 4.1.1.3. 注解实现事务管理
  5. 5. SSH 框架整合
    1. 5.1. Spring 和 Hibernate 整合
      1. 5.1.1. 零障碍整合
      2. 5.1.2. 将 Hibernate 参数配置到 Spring
      3. 5.1.3. HibernateTemplate 用法
    2. 5.2. Spring 和 Struts 整合
      1. 5.2.1. 自动装配 Service
      2. 5.2.2. Action由 Spring 管理
      3. 5.2.3. 结论
  6. 6. 延迟加载问题
  7. 7. 注解整合

Spring事务管理

在Java开发中, 事务管理代码放到业务层

事务管理 API

  • PlatformTransactionManager 提供事务管理方法, 核心接口

    1
    2
    3
    4
    5
    6
    
    // 提交事务
    void commit(TransactionStatus status);
    // 根据事务定义信息,获得当前状态 
    TransactionStatus getTransaction(TransactionDefinition definition);
    // 回滚事务
    void rollback(TransactionStatus status)
    
  • TransactionDefinition 事务的定义信息(隔离级别, 传播行为, 超时时间, 只读)

    • ISOLATION_xxx 事务隔离级别
    • PROPAGATION_xxx 事务传播行为
    • int getTimeout() 获得超时信息
    • boolean isReadOnly() 判断事务是否只读
  • TransactionStatus 事务具体行为, 每一个时刻点, 事务具体状态信息

关系: PlatformTransactionManager 根据 TransactionDefinition 进行事务管理, 管理过程中事务存在多种状态, 每个状态信息通过 TransactionStatus 表示

PlatformTransactionManager

Spring 为不同的持久化框架提供了不同的PlatformTransactionManager接口实现 , 针对不同的持久层技术, 要选用对应的事务管理器

不同平台事务管理器实现 说明
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或iBatis 进行持久化数据时使用
org.springframework.orm.hibernate3.HibernateTransactionManager 使用Hibernate3.0版本进行持久化数据时使用
org.springframework.orm.jpa.JpaTransactionManager 使用JPA进行持久化时使用
org.springframework.jdo.JdoTransactionManager 当持久化机制是Jdo时使用
org.springframework.transaction.jta.JtaTransactionManager 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用

事务的隔离级别

四大特性: ACID, 原子性, 一致性, 隔离性, 持久性

隔离性引发并发问题:脏读 不可重复读, 幻读

  • 脏读 一个事务读取另一个事务 未提交数据
  • 不可重复读 一个事务读取另一个事务 已经提交 update 数据
  • 虚读 一个事务读取另一个事务 已经提交 insert 数据

事务的隔离级别: 为了解决事务隔离性引发的问题

  • DEFAULT 默认级别 mysql REPEATABLE_READ 、 oracle READ_COMMITTED
  • READ_UNCOMMITED 导致所有问题发生
  • READ_COMMITTED 防止脏读、 发生不可重复读和虚读
  • REPEATABLE_READ 防止脏读、不可重复读,发生虚读
  • SERIALIZABLE 防止所有并发问题

事务的传播行为

为什么要有事务的传播行为?(Why)

实际开发中, 业务层方法间相互调用, 如在删除客户信息时, 要先删除订单信息
那么删除订单出错,客户要不要删除?

什么是事务的传播行为?(what)

一个业务层事务调用令一个业务层事务, 事务间之间关系如何处理

七种传播行为:

  • PROPAGATION_REQUIRED 支持当前事务, 如果不存在 就新建一个
  • PROPAGATION_SUPPORTS 支持当前事务, 如果不存在,就不使用事务
  • PROPAGATION_MANDATORY 支持当前事务, 如果不存在,抛出异常
  • PROPAGATION_REQUIRES_NEW 如果有事务存在, 挂起当前事务, 创建一个新的事务
    • 生成订单, 发送通知邮件, 通知邮件会创建一个新的事务, 如果邮件失败, 不影响订单生成
  • PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
  • PROPAGATION_NEVER 以非事务方式运行, 如果有事务存在, 抛出异常
  • PROPAGATION_NESTED 如果当前事务存在, 则嵌套事务执行
    • 依赖于 JDBC3.0 提供 SavePoint 技术
    • 删除客户 删除订单, 在删除客户后, 设置SavePoint, 执行删除订单, 删除订单和删除客户在同一个事务, 删除订单失败, 事务回滚 SavePoint , 由用户控制是事务提交 还是 回滚

事务管理方式

编程式事务管理

  • 在代码中通过 TransactionTemplate 手动进行事务管理, 在实际开发中很少被用到

声明式事务管理

  • 在配置文件中, 对 Bean 的方法进行事务管理, 基于AOP思想, 无需写代码

实际案例: 转账

如果没有进行事务管理, JdbcTemplate DAO 每一个操作, 都是一个事务

数据库脚本

1
2
3
4
5
6
7
8
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `money` double DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `account` VALUES ('1', 'aaa', '1000');
INSERT INTO `account` VALUES ('2', 'bbb', '1000');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- 最全的spring 模板 -->
<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: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/context
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop.xsd
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 导入外部属性文件, 配置连接池 -->
    <bean id="accountService" class="zhpooer.AccountServiceImpl">
         <property name="accountDao" ref="accountDao"></property>
         <property name="transactionTemplate" ref="transactionTemplate"> </property>
    </bean>
    <bean id="accountDao" class="zhpooer.AccountDaoImpl">
    <!-- 将连接池注入给DAO, JdbcTemplate会自动 创建 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 编程式事务管理配置 -->
    <!-- 事务管理模板 -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="tractionManager"> </property>
    </bean>
    <!-- 事务管理器 -->
    <!-- org.springframework.jdbc.datasource.DataSourceTransactionManager 用来管理jdbc事务操作 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSrouce"> </property>
    </bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class AccountServiceImpl implements AccountService{
    // 自动注入
    private AccountDao accountDao;
    private TransactionTemplate transactionTemplate;
    
    // 转账
    public void transfer(String outAccount, String inAccount, Double money) {
            // 编程式事务管理, 使用事务模板管理事务
        transactionTemplate.execute(new TransactionCallbackWithoutResult(){
            @Override
            protected void doInTransactionWithoutResult(){
                accountDao.outMoney(outAccount, money);
                accountDao.inMoney(inAccount, money);
            }
        });
    }
}

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    public void outMoney(String outAccount, Double money) {
        String sql = "update account set money = money-? where name=?";
        getJdbcTemplate().update(sql, money, outAccount);
    }

    public void inMoney(String inAccount, Double money) {
        String sql = "update account set money = money+? where name=?";
        getJdbcTemplate().update(sql, money, inAccount);
    }
}

声明式事务管理

TransactionProxyFactoryBean

通过 TransactionProxyFactoryBean 对业务类创建代理, 实现声明式事务管理, 无需修改 Service 代码

缺点, 需要为每个Bean 都创建单独代理对象,开发量巨大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- 事务管理器 -->
<bean id="transactionManager" class="">
    <proerty name="dataSource" ref="dataSource">
    </proerty>
</bean>
<!-- 为目标Servicee创建代理 -->
<bean name="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <!-- 目标 -->
    <property name="target" ref="accountService"> </property>
    <!-- 针对接口代理 -->
    <property name="proxyInterfaces" value="zhpooer.AccountService"></property>
    <!-- 增强 事务管理 -->
    <property name="transactionManager" ref="transactionManager"></property>
    <!-- 事务管理属性 -->
    <property name="transactionAttributes">
        <props>
           <!-- key 就是方法名  -->
           <!-- value prop格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception -->
           <!-- PROPAGATION 事务传播行为 -->
           <!-- ISOLATION, 事务隔离级别 -->
           <!-- readOnly  表示事务是只读的,不能进行修改操作  -->
           <!-- -Exception 发生这些异常事务进行回滚(默认发生任何异常事务都会回滚) -->
            <!-- +Exception 事务将忽略这些异常,仍然提交事务  -->
            <prop key="transfer"> PROPAGATION_REQUIRED, readOnly, +java.lang.ArithmeticException </prop>
        </props>
    </property>
</bean>
<!-- 注入代理对象: @Qualifier("accountServiceProxy") -->

tx, 自动代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 定义事务管理增强 -->
<tx:advice id="txAdvicd" transaction-manager="transactionManager">
    <tx:attribute>
        <!--
        name="transfer" 事务管理方法名
        isolation="DEFAULT" 默认隔离级别
        qpropagation="REQUIRED"  默认传播行为
        read-only="false"  是否只读
        no-rollback-for=""  发生异常不会滚  类似+Exception
        rollback-for=""  发生异常回滚 类似-Exception
        timeout="-1"  不超时
        -->
        <tx:method name="transfer" isolatioin="DEFAULT" propagation="REQUIRED"
                   read-only="false" timeout="-1"></tx:method>
    </tx:attribute>
</tx:advice>
<!-- 使用Aop 进行自动代理 -->
<aop:config>
    <!-- 定义切点 -->
    <aop:pointcut expression="execution(public * * (..))" id="mypointcut"></aop:pointcut>
    <!-- 定义切面 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut"></aop:advisor>
</aop:config>

注解实现事务管理

  1. 在需要管理的类或者方法添加 @Trasactional

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    // isolation 隔离级别
    // propagation 传播行为
    // readOnly 是否只读
    // noRollbackFor 发生异常不回滚
    // rollbackFor 发生异常回滚
    // timeout 超时时间 -1 不超时
    
    @Transactional(isolation=Isolation.DEFAULT,propagation=,
                   readyOnly=, noRollbackFor=, rollbackFor, timeout=)
    public void transfer(){}
    
  2. 在 applicationContext.xml

    1
    
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    

SSH 框架整合

导入Jar包, 及配置文件

  • 表现层框架 struts2, struts2-json-plugin.jar, struts2-spring-plugin.jar, struts2-conversion.jar(注解), 以及 web.xml(filter), struts2.xml
  • 业务层 spring3

    1
    2
    3
    4
    
    配置 web.xml
    <listener>
        <listener-class> ContextLoaderListner </listener-class>
    </listener>
    
  • 持久层框架 hibernate3

Spring 和 Hibernate 整合

零障碍整合

通过 Spring 提供 LocalSessionFactoryBean, 注解引入hibernate配置文件, 在 Spring 容器中获得 SessionFactory对象, 将 SessionFactory, 注入到 DAO 程序

图书添加

移动脚本

1
2
3
4
5
create table(
    id int not null AUTO_INCREMENT PRIMARY key,
    bookname varchar(20) not null,
    price double not null
);
1
2
3
4
5
6
7
8
9
10
public class Book {
     private Integer id;
     private String bookname;
     private double price;
}

public class BookDao extends HibernateDaoSupport{
    
    public void saveBook(){}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!-- hibernate.cfg.xml -->
<mapping resource="domain.Book.hbm.xml"/>

<!-- Book.hbm.xml -->
<hibernate-mapping>
    <class name="domain.Book" table="book" catalog="">
        <id name="id">
            <generator class="identity"> </generator>
        </id>
        <property name="bookname"> </property>
        <property name="price"> </property>
    </class>
</hibernate-mapping>

<!-- applicationContext.xml -->
<!-- 配置 sessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <!-- 加载hibernate配置文件 -->
    <property name="configLocation" value="classpath:hibernate.cfg.xml">  </property>
</bean>
<!-- 注入 sessionFactory -->
<bean id="bookDao" class="dao.BookDao">
    <property name="sessionfactory" ref="sessionfactory"> </property>
</bean>

<bean id="bookService" class="service.BookService">
    <property name="bookDao" ref="bookDao"> </property>
</bean>
<tx:advice id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <tx:attribute>
        <tx:method name="*"> </tx:method>
    </tx:attribute>
</tx:advice>
<aop:config>
    <aop:advisor advice-ref="transactionManager" pointcut="execution()"> </aop:advisor>
</aop:config>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// BookDao.java
// HibernateTemplate 常用 api
public void saveBook(Book book){
    this.getHibernateTemplate().save(book);
}
public void updateBook(Book book) {
    this.getHibernateTemplate().update(book);
}
public void deleteBook(Book book) {
    this.getHibernateTemplate().delete(book);
}
public void findById(Integer id) {
    return this.getHibernateTemplate().get(Book.class, id);
}p
public List<Book> findAll(){
    // this.getSession().createQuery("from Book").list();
    return this.getHibernateTemplate().find("from Book");
}
public Book findByName(String name){
    //  this.getSession().createQuery("from Book where name=?").setParameter(0, name).uniqueResult();
    return this.getHibernateTemplate().find("from Book where name=?", name);
}

业务层

1
2
3
4
5
6
7
8
9
public class BookService{
    private BookDao bookDao;
    public void saveBook(Book book) {
        bookDao.saveBook(book);
    }
    public List<Book> findAllBooks(){
        return bookDao.findAll();
    }
}

将 Hibernate 参数配置到 Spring

将 hibernate框架的所有参数, 都配置到 applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!-- applicationContext.xml -->
<context:property-placeholder location="classpath:"></context:property-placeholder>
<!-- c3p0连接池 -->
<bean id="dataSource" class="com.ComboPooledDataSource">
    <!-- any other configuration  -->
</bean>

<!-- 配置 sessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
    <property name="hibernateProperties">
        <props>
            <!-- 数据库方言 -->        
            <prop key="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </prop>
            <prop key="hibernate.hbm2dll.auto">update </prop>
            <prop key="hibernate.format_sql">true </prop>
            <prop key="hibernate.show_sql">true </prop>
        </props>
    </property>
    <!-- 配置 hbm 文件 -->
    <property name="mappingResources | mappingLocations | mappingDirectoryLocations">
        <!-- mappingResources -->
        <list>
            <value>cn/zhpooer/Person.hbm.xml </value>
        </list>
        
        <!-- mappingLocations -->
        <list>
            <value>classpath: cn/zhpooer/Person.hbm.xml </value>
        </list>
        
        <!-- mappingDirectoryLocations, 查找所有目录下的配置文件 -->
        <list>
            <value>classpath: cn/zhpooer/domain </value>
        </list>
    </property>
</bean>

HibernateTemplate 用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
template.save(obj);
template.update(obj);
template.saveOrUpdate();
template.delete(obj);
template.get(class, id);
template.load(class, id)

// 查询
template.find(hql, Object... args); // QBC查询, 等价于 session.createQeury(hql);

template.findByCriteria(detachedCriteria) // 完全面向对象查询
template.findByNamedQuery(queryName) // 命名查询, 查询语句写入配置文件, 方便项目管理

public void testQBC {
    HibernateTemplate template = new HibernateTemplate();
    DetachedCriteria criteria = DetachedCriteria.forClass(Book.class); // 生成 select * from book;
    criteria.add(Restrictions.like("bookname", "%为什么%"));
    List<Book> books = template.findByCriteria(criteria);
}
/* 配置文件
* <hibernate-mapping>
*     <!-- 配置 hql -->
*     <query name="book.findbyname"> from book where bookname like ?</query>
*     <!-- 配置 sql -->
*     <sql-query name="book.findbynamesql">
*         <return class="domain.Book"></return> <!-- 将结果集封装到book对象 -->
*         select * from book where bookname like ?
*     </sql-query>
* </hibernate-mapping>
*/ 
public void testFindByNamedQuery(){
    List<Book> books = template.findByNamedQuery("book.findbyname", "%为什么%")

    List<Book> books = template.findByNamedQuery("book.findbynamesql", "%为什么%")
}

Spring 和 Struts 整合

Struts2 整合 Spring 框架原理: 修改 struts2 默认对象工厂(struts -> spring), 导入 struts2-spring-plugin

自动装配 Service

由 struts2 自己装配Action, 再由 Spring

1
2
3
4
5
6
7
8
9
10
11
12
public class BookaddAction extends ActionSupport{
    private Book book = new Book();
    // 自动装配, 不需要提供任何配置, 只需要在Action里提供 service的 set方法
    // 原理: struts配置文件中
    // `struts.objectFactory.spring.autoWire = true`(根据名字自动注入), 生效 

    private BookService bookService;
    public String execute(){
        pringln("添加图书")
        return NONE;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
<form action="bookadd.action" method="post">
    <input type="text" name="bookname"/>
    <input type="text" name="price"/>
    <input type="submit"/>
</form>

<struts>
    <package name="default" namespace="/" extends="struts-default">
        <action name="bookadd" class=BookaddAction"">
        </action>
    </package>
</struts>

Action由 Spring 管理

将 struts2 的Action配置spring管理的Bean对象

  1. 将Action配置为 Spring 中的一个Bean对象

    1
    2
    3
    4
    5
    
    <!-- applicationContext.xml 加入ActionBean -->
    <!-- 必须设置为 prototype -->
    <bean id="bookaddAction" class="action.BookaddAction" scope="prototype">
        <property name="bookService" ref="bookService"></property>
    </bean>
    
  2. Struts.xml 配置 Spring bean 的 id, 作为 class 的属性

    1
    2
    3
    4
    5
    
    <!-- 不是真实类名, 只是只给伪类名 -->
    <package>
        <action name="bookadd" class="bookaddAction">
        </action>
    </package>
    

结论

第一种整合方式, Action 由 Struts2 自己管理, Service对象采用自动装配

第二种整合方式, Action 由 spring 自己管理, 依赖注入service对象, struts2 需要配置伪类名

第二种方式, 可以对 Action 进行 AOP 增强

延迟加载问题

懒加载对象, 但是对象已经脱管, 报异常 no session

解决方案

  1. 设置为立即加载 lazy=false, 缺点, 每次查询客户, 都要查询订单
  2. 在业务层, 事务关闭前手, 动初始化 Hibernate.initialize(customer.getOrders()), 缺点, 需要写代码
  3. OpenSessionView, session 不随事务关闭而关闭, 将 session 延迟到表现层, 存在性能问题, 通过配置, 无需编码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    <!-- web.xml -->
    <!-- 在Struts2的过滤器前, 配置 OpenSessionInViewFilter -->
    <filter>
        <filter-name>openfilter</filter-name>
        <filter-class>org.springframework.orm.hiberante3.support.OpenSessionInViewFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>openfilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

注解整合

导入 struts-conversion-plugin.jar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// hiberante注解 是对 jpa注解的扩展
@Entity
@Table(name="book", catalog="")
public class Book {
    @Id
    @GeneratedValue(strategy = GeneratedType.IDENTITY)
    private Integer id;
    // 如果列名和属性名不一致, 可以不写
    @Column(name="", nullable=true)
    private String bookname;
    private double price;
}
@Repository("bookDao")
public class BookDao {
    @Resource(name="hiberanteTemplate")
    private HiberanteTemplate template;
}
@Controller("bookaddAction")
@Scope("prototype")
@Namespace("/")
@ParentPackage("struts-default")
public class BookaddAction{
    @Action("bookadd")
    public String execute(){
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--  AnnotationSessionFactoryBean 支持注解功能-->
<bean id="sessionFactory" class="org.springframework.orm.hiberante3.annotation.AnnotationSessionFactoryBean ">
    <!-- 其他配置同上  -->
    <property name="packageToScan">
        <list>
            <value>cn.zhpooer</value>
        </list>
    </property>
</bean>
<context:component-scan base-package="cn.itcast, io.zhpooer."/>
<context:annotation-config/>
<!-- 注解事务管理 -->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSrouce"> </property>
</bean>
<tx: annotation-driven transaction-manager="transactionManager"/>
文章目录
  1. 1. Spring事务管理
    1. 1.1. 事务管理 API
    2. 1.2. PlatformTransactionManager
  2. 2. 事务的隔离级别
  3. 3. 事务的传播行为
  4. 4. 事务管理方式
    1. 4.1. 实际案例: 转账
      1. 4.1.1. 声明式事务管理
        1. 4.1.1.1. TransactionProxyFactoryBean
        2. 4.1.1.2. tx, 自动代理
        3. 4.1.1.3. 注解实现事务管理
  5. 5. SSH 框架整合
    1. 5.1. Spring 和 Hibernate 整合
      1. 5.1.1. 零障碍整合
      2. 5.1.2. 将 Hibernate 参数配置到 Spring
      3. 5.1.3. HibernateTemplate 用法
    2. 5.2. Spring 和 Struts 整合
      1. 5.2.1. 自动装配 Service
      2. 5.2.2. Action由 Spring 管理
      3. 5.2.3. 结论
  6. 6. 延迟加载问题
  7. 7. 注解整合