Spring第三天

Spring第三天

整体课程安排(3天+2天):

第一天:Spring框架入门、IoC控制反转的配置管理、Spring Web集成、Spring Junit集成。

第二天:Spring AOP面向切面编程、AspectJ的集成配置、JdbcTemplate工具类。

第三天:Spring声明式事务管理、Spring和Struts2、Hibernate的整合

第四天、第五天:综合练习,熟悉SSH整合开发、jQuery Ajax、分页

今天的主要内容:

  1. Spring的事务管理机制(3个核心接口对象)
  2. 声明式事务管理案例-转账(xml-tx、aop、注解@Transactional)
  3. SSH的整合(SSH jar包和配置文件的导入、Spring(大管家)整合Hibernate(hibernateTemplate操作数据库)、Struts2整合Spring、延迟加载问题)

课程目标:

  1. 声明式事务的配置编写(事务的传播行为等几个概念,xml,注解写法)
  2. SSH整合(整合原理)
  1. Spring的事务管理机制

Spring事务管理高层抽象主要包括3个接口,Spring的事务主要是由他们共同完成的:

  • PlatformTransactionManager:事务管理器—主要用于平台相关事务的管理
  • TransactionDefinition:    事务定义信息(隔离、传播、超时、只读)—通过配置如何进行事务管理。
  • TransactionStatus:事务具体运行状态—事务管理过程中,每个时间点事务的状态信息。
  1. PlatformTransactionManager事务管理器

参考:spring-framework-3.2.0.RELEASE/docs/javadoc-api/index.html

该接口提供三个方法:

  • commit:提交事务
  • rollback:回滚事务
  • getTransaction:获取事务状态

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实现来管理事务,在一个事务跨越多个资源时必须使用

  • DataSourceTransactionManager针对JdbcTemplate、MyBatis 事务控制 ,使用Connection(连接)进行事务控制 :

开启事务 connection.setAutoCommit(false);

提交事务 connection.commit();

回滚事务 connection.rollback();

  • HibernateTransactionManager针对Hibernate框架进行事务管理, 使用Session的Transaction相关操作进行事务控制 :

开启事务 session.beginTransaction();

提交事务 session.getTransaction().commit();

回滚事务 session.getTransaction().rollback();

事务管理器的选择?

用户根据选择和使用的持久层技术,来选择对应的事务管理器。

  1. TransactionDefinition事务定义信息

用来定义事务相关的属性的,给事务管理器用。

参考:spring-framework-3.2.0.RELEASE/docs/javadoc-api/index.html

该接口主要提供的方法:

  • getIsolationLevel:隔离级别获取
  • getPropagationBehavior:传播行为获取
  • getTimeout:获取超时时间(事务的有效期)
  • isReadOnly 是否只读(保存、更新、删除—对数据进行操作-变成可读写的,查询-设置这个属性为true,只能读不能写),事务管理器能够根据这个返回值进行优化。

这些事务的定义信息,都可以在配置文件中配置和定制。

  1. 事务的隔离级别IsolationLevel

隔离级别

含义

DEFAULT

使用后端数据库默认的隔离级别(spring中的的选择项)

READ_UNCOMMITED

允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读

READ_COMMITTED

允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生

REPEATABLE_READ

对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。

SERIALIZABLE

完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。

脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。

不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,"可重复读"在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。

幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。

 

事务四大特性 ACID ---隔离性引发问题 ---- 解决事务的隔离问题 隔离级别

Mysql 默认隔离级别 REPEATABLE_READ

Oracle 默认隔离级别 READ_COMMITTED

多学一招:什么是ACID原则:

参考:

百度百科http://baike.baidu.com/link?url=EPDxwsYxObXZAmy1WEEhuj1_Hr3b3UMQi4SGYOPVcWcvPMzFteL2MRU39khXZgHB9NLzpqpRQoiI6OFCS5WkeJ2nyK-ozmNcXb-aRocRMYa

  1. 事务的传播行为PropagationBehavior

什么是事务的传播行为? 有什么作用?

事务传播行为用于解决两个被事务管理的方法互相调用问题

业务层两个方法面临的事务问题:

* 有些时候需要处于同一个事务(删除用户删除完成之后,需要同时删除用户对应的订单,需要事务回滚,例如商场工作人员删除订单业务),

* 有些时候不能在同一个事务(取款是一个事务操作,打印凭条是一个事务操作,例如ATM取款业务) !

//开启事务一

//事务一

//提交事务一

//开启事务2

//事务2

//提交事务2

//开启事务

//事务一

//事务2

//提交事务

事务的传播行为的7种类型:

事务传播行为类型

说明

PROPAGATION_REQUIRED

支持当前事务,如果不存在 就新建一个

PROPAGATION_SUPPORTS

支持当前事务,如果不存在,就不使用事务

PROPAGATION_MANDATORY

支持当前事务,如果不存在,抛出异常

PROPAGATION_REQUIRES_NEW

如果有事务存在,挂起当前事务,创建一个新的事务

PROPAGATION_NOT_SUPPORTED

以非事务方式运行,如果有事务存在,挂起当前事务

PROPAGATION_NEVER 

以非事务方式运行,如果有事务存在,抛出异常

PROPAGATION_NESTED

如果当前事务存在,则嵌套事务执行

只对DataSourceTransactionManager 起效

主要分为三大类:

  • PROPAGATION_REQUIRED(默认值)、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY

    支持当前事务, A调用B,如果A事务存在,B和A处于同一个事务 。

事务默认传播行为 REQUIRED。最常用的。

  • PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER

不会支持原来的事务 ,A调用B, 如果A事务存在, B肯定不会和A处于同一个事务。

常用的事务传播行为:PROPAGATION_REQUIRES_NEW

  • PROPAGATION_NESTED

嵌套事务 ,只对DataSourceTransactionManager有效 ,底层使用JDBC的SavePoint机制,允许在同一个事务设置保存点,回滚保存点

附录:嵌套事务的示例:

Connection conn = null;

try {

conn.setAutoCommit(false);

Statement stmt = conn.createStatement();

1.stmt.executeUpdate("update person set name='888' where id=1");

 

2.Savepoint savepoint = conn.setSavepoint();

try{

3.conn.createStatement().executeUpdate("update person set name='222' where sid=2");

}catch(Exception ex){

conn.rollback(savepoint);

}

stmt.executeUpdate("delete from person where id=9");

conn.commit();

stmt.close();

} catch (Exception e) {

conn.rollback();

}finally{

try {

     if(null!=conn && !conn.isClosed()) conn.close();

} catch (SQLException e) { e.printStackTrace(); }

}

}

【面试题】REQUIRED、REQUIRES_NEW、NESTED 区分

    REQUIRED:只有一个事务(默认,推荐)

    REQUIRES_NEW:存在两个事务 ,如果事务存在,挂起事务,重新又开启了一个新的事务

    NESTED 嵌套事务,事务可以设置保存点,回滚到保存点 ,选择提交或者回滚

  1. TransactionStatus 事务状态

事务运行过程中,每个时间点 事务状态信息 !

flush(),给hibernate使用,底层发出sql的

hasSavepoint():判断是否有保留点

isCompleted():判断事务是否结束

isNewTransaction():判断当前事务是否是新开的一个事务。

isRollbackOnly():判断事务是否只能回滚

setRollbackOnly():设置事务是否回滚

事务的结束:必须通过commit 确认事务提交, rollback 作用标记为回滚。

数据库操作中,如果只是回滚,后面不操作,数据库在关闭连接的时候,自动发出了commit。

    try {

        操作

    } catch (){

        rollback

    } finally {

        commit

}

【三个事务超级接口对象之间的关系】

1)首先用户管理事务,需要先配置TransactionDefinition(事务定义信息、事务的管理方案);

2)然后根据TransactionDefinition,通过TransactionManager(事务管理器)进行事务管理;

3)最后事务运行过程中,每个时刻都可以通过获取TransactionStatus(事务状态)来了解事务的运行状态。

  1. Spring事务管理两种方式

Spring 支持两种方式事务管理

  • 一:编程式的事务管理

通过TransactionTemplate手动管理事务

在实际应用中很少使用,原因是要修改原来的代码,加入事务管理代码 (侵入性 )

参考文档:http://www.yiibai.com/spring/programmatic_management.html

  • 二:使用XML或注解配置声明式事务

* Spring的声明式事务是通过AOP实现的(环绕通知)

* 开发中经常使用(代码侵入性最小)--推荐使用!

  1. 声明式事务管理案例-转账(xml、注解)

    1. 编写转账案例,引出事务管理问题

需求:账号转账,Tom账号取出1000元,存放到Jack账号上

数据表和测试数据准备:

建表脚本(MySQL):

第一步:创建表t_account

CREATE TABLE `t_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 `t_account` VALUES (1, 'Tom', 1000);

INSERT INTO `t_account` VALUES (2, 'Jack', 1100);

INSERT INTO `t_account` VALUES (3, 'Rose', 1200);

第三步:查看测试数据:

第一步:新建web工程,spring3_day03_transaction

第二步:导入jar(核心4+2,数据库驱动,c3p0连接池、测试包,jdbc和事务2个)和applicationContext.xml配置文件和log4j.properties文件和db.properties文件:

建包: cn.itcast.spring.dao

第三步:创建IAccountDao接口

public interface IAccountDao {

 

    //(存入)转入

    public void in(String name,Double money);

    

    //(取出)转出

    public void out(String name,Double money);

}

创建AccounDaoImpl实现类,实现了IAccountDao接口

//账户操作持久层

//技术方案:jdbctempate

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

    

    //(存入)转入

    public void in(String name,Double money){

        String sql="update t_account set money = money+ ? where name = ?";

        super.getJdbcTemplate().update(sql, money,name);

    }

    

    //(取出)转出

    public void out(String name,Double money){

        String sql="update t_account set money = money- ? where name = ?";

        super.getJdbcTemplate().update(sql, money,name);

    }

}

第四步:建立service层,创建IAccountService接口,编写转账的业务代码:

建包: cn.itcast.spring.service

public interface IAccountService {

 

    void transfer(String outName,String inName,Double money);

 

}

创建AccountServiceImpl实现类,实现了IAccountService接口,编写转账的业务操作

//掌握操作的业务层

public class AccountServiceImpl implements IAccountService{

    

    //注入dao

    private IAccountDao accountDao;

 

    public void setAccountDao(IAccountDao accountDao) {

        this.accountDao = accountDao;

    }

 

 

    //转账操作的业务逻辑

    public void transfer(String outName,String inName,Double money){

        //调用dao

        //先取出

        accountDao.out(outName, money);

        //再转入

      

        accountDao.in(inName, money);

    }

 

}

第五步:使用SpringTest进行测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations={"classpath:applicationContext.xml"})

public class SpringTest {

    //注入测试的service

    @Autowired

    private IAccountService accountService;

    

    //需求:账号转账,Tom账号取出1000元,存放到Jack账号上

    @Test

    public void testTransfer(){

        accountService.transfer("Tom", "Jack", 1000d);

        System.out.println("转账成功!");

    }

 

}

但是发现问题:

事务管理问题:在Service层没有事务的情况下,如果出现异常,则会转账不成功,数据异常。

扩展:如果不配置事务,那么每一个数据库的操作都是单独的一个事务。

  1. XML配置方式添加事务管理(tx、aop元素)

【操作思路】:

1、    确定目标:需要对AccountService 的 transfer方法,配置切入点

2、    需要Advice (环绕通知),方法前开启事务,方法后提交关闭事务

3、    配置切面和切入点

第一步:导入aop相关的包(4个),引入约束名称空间(aop和tx 的名称空间)

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

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

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

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">

配置本地提示:

配置Advice通知:

Spring为简化事务的配置,提供了<tx:advice>来配置事务管理,也可以理解为该标签是spring为你实现好了的事务的通知增强方案。

第一步:导入jar包:

其中:

com.springsource.org.aopalliance-1.0.0.jar:aop切面编程

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar:注解开发切面

spring-aop-3.2.0.RELEASE.jar:aop切面编程

spring-aspects-3.2.0.RELEASE.jar:注解开发切面

spring-tx-3.2.0.RELEASE.jar:事务处理

第二步:配置spring容器,applicationContext.xml文件

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

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

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

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">

 

<!-- 引入外部属性配置文件-->

    <context:property-placeholder location="classpath:db.properties"/>

      

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

    <!-- c3p0连接池 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

        <property name="driverClass" value="${jdbc.className}" />

        <property name="jdbcUrl" value="${jdbc.url}" />

        <property name="user" value="${jdbc.user}" />

        <property name="password" value="${jdbc.password}" />

    </bean>

 

    

    <!-- 第一步:定义具体的平台事务管理器(DataSource事务管理器) -->

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

        <!-- 注入数据源 -->

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

    </bean>

<!-- 第二步:定义通知,通知中要处理的就是事务 -->

    <tx:advice id="txAdvice" transaction-manager="transactionManager">

        <!-- 配置事务的属性定义 -->

        <tx:attributes>

            <!-- 配置具体的方法的事务属性

            isolation//事务的隔离级别,默认是按数据库的隔离级别来

            propagation//事务的传播行为,默认是同一个事务

            timeout="-1":事务的超时时间,默认值使用数据库的超时时间。

            read-only="false":事务是否只读,默认可读写。

            rollback-for:遇到哪些异常就回滚,其他的都不回滚

            no-rollback-for:遇到哪些异常不回滚,其他的都回滚。和上面互斥的

             -->

            <tx:method name="transfer" isolation="DEFAULT"    propagation="REQUIRED" timeout="-1" read-only="false"/>

            

            <!-- 支持通配符

                要求service中 方法名字必须符合下面的规则

             -->

            <tx:method name="save*"/>

            <tx:method name="update*"/>

            <tx:method name="delete*"/>

            <tx:method name="find*" read-only="true"/>

        </tx:attributes>

    </tx:advice>

    <!-- 第三步:配置切入点,让通知关联切入点,即事务控制业务层的方法 -->

    <aop:config>

        <!-- 切入点 -->

        <aop:pointcut expression="bean(*Service)" id="txPointcut"/>

        <!-- 切面 -->

        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>

    </aop:config>

    

    <!-- dao -->

    <bean id="accountDao" class="cn.itcast.spring.dao.AccountDaoImpl">

        <!-- 注入数据源,才拥有jdbctemplate -->

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

    </bean>

    <!-- 业务层 -->

    <bean id="accountService" class="cn.itcast.spring.service.AccountServiceImpl">

        <!-- 注入dao -->

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

    </bean>

    

</beans>

使用SpringTest.java测试:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations={"classpath:applicationContext.xml"})

public class SpringTest {

    //注入测试的service

    @Autowired

    private IAccountService accountService;

    

    //需求:账号转账,Tom账号取出1000元,存放到Jack账号上

    @Test

    public void testTransfer(){

        accountService.transfer("Tom", "Jack", 1000d);

        System.out.println("转账成功!");

    }

 

}

数据正常!

【声明式事务处理的原理图】

【注意】

如果不配置,则走默认的事务(默认事务是每个数据库操作都是一个事务,相当于没事务),所以我们开发时需要配置事务。

【补充了解】:

rollback-for属性:

参考:spring3_day1_课前资料/Spring2.5-中文参考手册.chm

注意事项:声明式事务处理对运行时异常有效,任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

测试:

applicationContext.xml:

遇到该异常事务不会回滚:

  1. 注解配置方式添加事务管理 @Transactional

步骤:

1.在需要管理事务的方法或者类上面 添加@Transactional 注解

2.配置注解驱动事务管理(事务管理注解生效的作用)(需要配置对特定持久层框架使用的事务管理器)

第一步:确定目标(bean的方法)

创建包cn.itcast.spring.anntx

在cn.itcast.spring.anntx.dao中创建IAccountDao和AccountDaoImpl类

  1. IAccountDao.java接口

    public interface IAccountDao {

     

        //(存入)转入

        public void in(String name,Double money);

        

        //(取出)转出

        public void out(String name,Double money);

    }

    (2)AccountDaoImpl.java类

    //账户操作持久层

    //技术方案:jdbctempate

    /**

    * @Repository("accountDao")

    * 相当于容易中定义<bean id="accountDao" class="cn.itcast.spring.anntx.dao.AccountDaoImpl"/>

    */

    @Repository("accountDao")

    public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

        

        //注入数据源

        ////@Autowired

        //private DataSource dataSource;//没有注入数据源成功~

        ////原理:放到属性上的的注解相当于,自动生成setter方法上加注解

        //@Autowired    //自动到spring的容器中寻找类型是参数类型(DataSource)的bean

        //public void setDataSource(DataSource dataSource){

        //    this.dataSource=dataSource;

        //}

        

        @Autowired//当初始化dao的时候,会调用该方法啊,通过set方法的形参注入数据源

        //方法名无所谓

        public void setSuperDataSource(DataSource dataSource){

            //调用父类的方法

            super.setDataSource(dataSource);        

        }

        

        //(存入)转入

        public void in(String name,Double money){

            String sql="update t_account set money = money+ ? where name = ?";

            super.getJdbcTemplate().update(sql, money,name);

        }

        

        //(取出)转出

        public void out(String name,Double money){

            String sql="update t_account set money = money- ? where name = ?";

            super.getJdbcTemplate().update(sql, money,name);

        }

    }

    在cn.itcast.spring.anntx.service中创建IAccountService和AccountServiceImpl类

  2. IAccountService接口

    public interface IAccountService {

     

        void transfer(String outName,String inName,Double money);

     

    }

  3. AccountServiceImpl类

    //掌握操作的业务层

    /**

    * @Service("accountService")

    * 相当于spring容器中定义:<bean id="accountService" class="cn.itcast.spring.anntx.service.AccountServiceImpl">

    */

    @Service("accountService")

    @Transactional//会对该类中,所有的共有的方法,自动加上事务--全局的设置,默认是可写

    public class AccountServiceImpl implements IAccountService{

        

        //注入dao

        @Autowired

        private IAccountDao accountDao;

     

        //转账操作的业务逻辑

    //    @Transactional//在方法上添加事务

        public void transfer(String outName,String inName,Double money){

            

            //调用dao

            //先取出

            accountDao.out(outName, money);

            int d = 1/0;

            //再转入

            accountDao.in(inName, money);

     

        }

        

        @Transactional(readOnly=true)//使用局部覆盖全局的

        public void findAccount(){

            System.out.println("查询帐号的信息了");

        }

     

    }

    第二步:创建applicationContext-tx.xml在applicationContext-tx.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:p="http://www.springframework.org/schema/p"

    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">

     

    <!-- 引入外部属性文件 -->

        <context:property-placeholder location="classpath:db.properties" />

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

        <!-- c3p0连接池 -->

        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

            <property name="driverClass" value="${jdbc.className}" />

            <property name="jdbcUrl" value="${jdbc.url}" />

            <property name="user" value="${jdbc.user}" />

            <property name="password" value="${jdbc.password}" />

        </bean>    

        <!-- 配置bean注解扫描 -->

        <context:component-scan base-package="cn.itcast.spring.anntx"/>

        

        <!-- 定义具体的平台事务管理器(DataSource事务管理器) -->

        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

            <!-- 注入数据源 -->

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

        </bean>

          

        

        <!-- 配置事务注解驱动 :识别事务的注解@tr。。。

        transaction-manager:具体的平台事务管理器

        -->

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

        <!-- 默认的平台事务管理器的名字叫:transactionManager,此时transaction-manager="transactionManager"可以不写 -->

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

        

    </beans>

    【注意】:数据源的注解注入 需要自己添加set方法

    (1)在需要管理事务的方法或者类上面 添加@Transactional 注解

    (2)配置事务的定义属性信息,在注解中直接配置:

    【扩展1】

    如果 @Transactional 标注在 Class 上面, 那么将会对这个 Class 里面所有的 public 方法都包装事务方法。等同于该类的每个公有方法都放上了@Transactional。

    如果某方法需要单独的事务定义,则需要在方法上加@Transactional来覆盖类上的标注声明。记住:方法级别的事务覆盖类级别的事务

    //掌握操作的业务层

    /**

    * @Service("accountService")

    * 相当于spring容器中定义:<bean id="accountService" class="cn.itcast.spring.anntx.service.AccountServiceImpl">

    */

    @Service("accountService")

    @Transactional()//会对该类中,所有的共有的方法,自动加上事务--全局的设置,默认是可写

    public class AccountServiceImpl implements IAccountService{

        

        //注入dao

        @Autowired

        private IAccountDao accountDao;

     

        //转账操作的业务逻辑

        @Transactional(readOnly=false)//在方法上添加事务

        public void transfer(String outName,String inName,Double money){

            

            //调用dao

            //先取出

            accountDao.out(outName, money);

            int d = 1/0;

            //再转入

            accountDao.in(inName, money);

     

        }

        

        @Transactional(readOnly=true)//使用局部覆盖全局的

        public void findAccount(){

            System.out.println("查询帐号的信息了");

        }

     

    }

    1. 小结-xml和注解的选择

    XML配置方式和注解配置方式 进行事务管理 哪种用的多?

    XML方式,集中式维护,统一放置到applicationContext.xml文件中,缺点在于配置文件中的内容太多。

    使用@Transactional注解进行事务管理,配置太分散,使用XML进行事务管理,属性集中配置,便于管理和维护

    注意:以后的service的方法名字的命名,必须是上面规则,否则,不能被spring事务管理。!!!!

    即以save开头的方法,update开头的方法,delete开头的方法,表示增删改的操作,故事务为可写

    以find开头的方法,表示查询,故事务为只读

    (1)xml方式小结

    (2)注解方式小结

    1. SSH的整合(Spring整合Hibernate、Struts2整合Spring、延迟加载问题)xml方式

    目标:体现spring的特性:粘合剂的作用(整合Hibernate,整合Struts2)。能让spring管理的都让其管理。

    新建 web工程 spring3_day03_ssh

    1. SSH jar包和配置文件的导入

    1. struts2框架(15个)

    学习版本: 2.3.15.3

    第一步:导入Jar包

  • 最基本jar包 apps/struts2-blank.war (13个 )

  • lib下插件扩展包

    struts2-json-plugin-2.3.15.3.jar 进行Ajax开发插件包

    struts2-convention-plugin-2.3.15.3.jar 约定扫描插件 (注解开发,xml开发的时候一定不要拷贝 )

    struts2-spring-plugin-2.3.15.3.jar 整合spring插件包 (1个)

第二步:web.xml配置文件

  • web.xml 配置核心Filter

<!-- struts2的前端控制器,这是struts2运行的核心 -->

    <filter>

        <filter-name>struts2</filter-name>

        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name>struts2</filter-name>

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

    </filter-mapping>

第三步:在src下创建struts.xml

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC

    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

    "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>

    <!-- 开发者模式:-->

<constant name="struts.devMode" value="true" />

    <!-- 主题样式 -->

    <constant name="struts.ui.theme" value="simple"/>

 

<package name="default" namespace="/" extends="struts-default">

        <!-- 图书管理 -->

        <!-- action还是struts自己管理,自动装配bean的机制 -->

        <action name="book_*" class="cn.itcast.ssh.web.action.BookAction" method="{1}">

            

        </action>

</package>

 

</struts>

  • 第四步:在src下,创建log4j.properties

### direct log messages to stdout ###

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.Target=System.out

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

 

### direct messages to file mylog.log ###

log4j.appender.file=org.apache.log4j.FileAppender

log4j.appender.file.File=c:mylog.log

log4j.appender.file.layout=org.apache.log4j.PatternLayout

log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

 

### set log levels - for more verbose logging change 'info' to 'debug' ###

 

log4j.rootLogger=info, stdout,file

  1. spring3框架 (13)

学习版本:spring3.2.x

第一步:导入Jar包

Spring3.2 开发最基本jar包

  • spring-beans-3.2.0.RELEASE.jar
  • spring-context-3.2.0.RELEASE.jar
  • spring-core-3.2.0.RELEASE.jar
  • spring-expression-3.2.0.RELEASE.jar
  • com.springsource.org.apache.commons.logging-1.1.1.jar
  • com.springsource.org.apache.log4j-1.2.15.jar

AOP开发

  • spring-aop-3.2.0.RELEASE.jar
  • spring-aspects-3.2.0.RELEASE.jar
  • com.springsource.org.aopalliance-1.0.0.jar
  • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

Spring Jdbc开发

  • spring-jdbc-3.2.0.RELEASE.jar

Spring事务管理

  • spring-tx-3.2.0.RELEASE.jar

Spring整合其他ORM框架

  • spring-orm-3.2.0.RELEASE.jar

Spring在web中使用

  • spring-web-3.2.0.RELEASE.jar

Spring整合Junit测试

  • spring-test-3.2.0.RELEASE.jar

总结:

  • Spring的lib目录 需要导入jar包 (11个 )

    基本4个

    Aop 2个

    Jdbc事务 2个

    Test 1个

    Web1 个

    整合ORM框架 1个 (整合hibernate :spring-orm.jar )

  • 日志 commons-logging + log4j (注意:已经在struts2 导入过
  • AOP第三方 (2个)

第二步:定义web.xml配置文件

  • web.xml 核心监听器,初始化容器

    <!-- spring的核心监听器:初始化spring容器 -->

    <listener>

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

    </listener>

<!-- spring的核心监听器:定义spring容器的位置,在src -->

    <context-param>

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

        <param-value>classpath:applicationContext.xml</param-value>

    </context-param>

第三步:在src下applicationContext.xml ,引入bean、context、aop、tx的约束

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

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

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

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">

  1. hibernate3框架 (10)

学习版本 : hibernate3.6.10.final

第一步:Jar包

解压根目录 hibernate3.jar

解压根目录lib equired*.jar

 

解压根目录libjpahibernate-jpa-2.0-api-1.0.1.Final.jar

数据库连接池c3p0

解压根目录liboptionalc3p0c3p0-0.9.1.jar

整合log4j :slf4j-log4j12-1.7.2.jar 、log4j-1.2.16.jar

使用二级缓存 Ehcache

 

数据库驱动 mysql-connector-java-5.0.8-bin.jar

总结:

  • hibernate3.jar 核心包 (1个)
  • lib/required 必须的 (5个 javassist 已经导入 )
  • 日志 slf4j 整合 log4j (1个)
  • lib/jpa (1个)
  • lib/optional/c3p0 连接池 (1个)
  • 数据库驱动 mysql(1个)

注意:多个框架整合时的jar冲突问题。

可以使用:,如果发现出现冲突,默认是用高版本的。

第二步:配置文件 ,在src下 hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC

    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <!-- JDBC基本连接参数 -->

    <session-factory>

        <!-- conntion配置-mysql -->

        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

        <property name="hibernate.connection.url">jdbc:mysql:///itcastspring</property>

        <property name="hibernate.connection.username">root</property>

        <property name="hibernate.connection.password">root</property>

        <!-- 配置方言-mysql -->

        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

        

        <!-- 常见其它配置 -->

        <property name="hibernate.show_sql">true</property> <!-- 控制台上打印SQL -->

        <property name="hibernate.format_sql">true</property> <!-- 控制台输出时,对SQL语句格式化 -->

        <!-- 测试环境 create/ create-drop 正式环境 update validate -->

        <property name="hibernate.hbm2ddl.auto">update</property> <!-- 自动建表 -->

        

        <!-- 引入映射配置 -->

        <mapping resource="cn/itcast/ssh/domain/Book.hbm.xml"/>

 

    </session-factory>

</hibernate-configuration>    

  1. Spring和Hibernate整合

  1. 创建数据表,编写实体映射

第一步:数据库脚本

用户名和密码都是root

第二步:建立cn.itcast.ssh.domain包,用来存放实体类Book.java和实体类的映射文件Book.hbm.xml:

(1)实体类Book.java

//po实体类

public class Book {

    private Integer id;//oid

    private String name;

    private Double price;

    

    public Integer getId() {

        return id;

    }

    public void setId(Integer id) {

        this.id = id;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public Double getPrice() {

        return price;

    }

    public void setPrice(Double price) {

        this.price = price;

    }

    

    public String toString() {

        return "Book [id=" + id + ", name=" + name + ", price=" + price + "]";

    }

      

    

 

}

(2)编写实体类的映射文件Book.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="cn.itcast.ssh.domain.Book" table="book">

        <id name="id">

            <generator class="native"/>

        </id>

        <property name="name"/>

        <property name="price"/>

    </class>

</hibernate-mapping>

第三步:配置hibernate.cfg.xml 引入映射

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC

    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <!-- JDBC基本连接参数 -->

    <session-factory>

    <!-- 理解为连接池 -->

        <!-- conntion配置-mysql -->

        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

        <property name="hibernate.connection.url">jdbc:mysql:///itcastspring</property>

        <property name="hibernate.connection.username">root</property>

        <property name="hibernate.connection.password">root</property>

        <!-- 配置方言-mysql -->

        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

        

        <!-- 常见其它配置 -->

        <property name="hibernate.show_sql">true</property> <!-- 控制台上打印SQL -->

        <property name="hibernate.format_sql">true</property> <!-- 控制台输出时,对SQL语句格式化 -->

        <!-- 测试环境 create/ create-drop 正式环境 update validate -->

        <property name="hibernate.hbm2ddl.auto">update</property> <!-- 自动建表 -->

        

        <!-- 引入映射配置 -->

        <mapping resource="cn/itcast/ssh/domain/Book.hbm.xml"/>

 

    </session-factory>

</hibernate-configuration>    

第四步:使用Junit测试连接数据库:

创建包cn.itcast.ssh.test,创建类HibernateTest.java进行测试

public class HibernateTest {

 

    @Test

    public void test(){

        Configuration configuration = new Configuration();

        //加载src下的hibernate.cfg.xml

        configuration.configure();

        //创建SessionFactory工厂

        SessionFactory sf = configuration.buildSessionFactory();

        //打开Session

        Session s = sf.openSession();

        //开启事务

        Transaction tr = s.beginTransaction();

        

        Book book = new Book();

        book.setName("降龙十八掌");

        book.setPrice(122d);

        s.save(book);

        

        //事务提交

        tr.commit();

        //session关闭

        s.close();

        

    }

}

以上是使用hibernate.cfg.xml文件的定义连接数据库的,那么能否修改成使用spring连接数据库呢?

  1. Spring整合Hibernate

大家都知道,Hibernate的核心是SessionFactory,并由SessionFactory创建Session,再由Session操作数据库。

思想:将Hibernate中SessionFactory对象,由Spring管理

    通过 spring-orm包 提供 LocalSessionFactoryBean 实现

方式一: 直接在spring中加载hibernate配置文件 (零障碍整合)

<!-- spring整合Hibernate:将session工厂交给spring管理,这是spring整合hibernate的核心 -->

    <!-- 方式一: 直接在spring中加载hibernate配置文件 (零障碍整合) -->

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

        

        <!-- 注入Hibernate的核心配置文件

        提示技巧:xxxLocation,一般都要加classpath -->

        <property name="configLocation" value="classpath:hibernate.cfg.xml"/>

    </bean>

测试:验证整合是否成功:删除book表,将项目部署tomcat,启动服务器,查看是否建表

原理:Web.xml 配置监听器 ---> 初始化spring容器 ---> 初始化hibernate配置--->初始化所有单例对象 ---> sessionFactory 建表

容器之间的关系原理:

方式二: (推荐)将hibernate参数配置到spring文件

    没有hibernate.cfg.xml配置文件 !!!完全由spring进行管理

经过分析: 需要三个方面的配置 (数据源配置、 常用属性配置、 hbm映射加载 )

  • 数据源配置(在applicationContext.xml中配置)

<!-- 引入外部属性配置文件 -->

    <context:property-placeholder location="classpath:db.properties"/>

    <!-- c3p0连接池 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

        <property name="driverClass" value="${jdbc.driverClass}"/>

        <property name="jdbcUrl" value="${jdbc.url}"/>

        <property name="user" value="${jdbc.username}"/>

        <property name="password" value="${jdbc.password}"/>

    </bean>

  • Hibernate常用属性配置

  • Hbm映射引入加载配置

<!-- 方式二: (推荐)将hibernate参数配置到spring文件 -->

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

        <!-- 1.数据源 -->

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

        <!-- 2.Hibernate的属性 -->

        <property name="hibernateProperties">

            <props>

            <!-- 设置方言 -->

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

                <!-- 设置打印sql语句 -->

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

                <!-- 格式化sql语句 -->

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

                <!-- 自动建表 -->

                <prop key="hibernate.hbm2ddl.auto">update</prop>

            </props>

        </property>        

<!-- 3.mapping映射 -->

        <property name="mappingResources">

            <list>

                <value>cn/itcast/ssh/domain/Book.hbm.xml</value>

            </list>

        </property>

    </bean>

测试:验证整合是否成功:删除book表,将项目部署tomcat,启动服务器,查看是否建表

  1. 编写Dao类 (数据层)

传统写法: 通过Hibernate的Session ,实现数据表操作

Spring 整合 Hibernate,提供 HibernateTemplate 简化原始Hibernate代码 (不用关心session,你只需要用就行了)

附录:

HibernateTemplate常用的API

  • void save(Object entity)
  • void update(Object entity)
  • void delete(Object entity)
  • <T> T get(Class<T> entityClass, Serializable id)
  • <T> T load(Class<T> entityClass, Serializable id)
  • List find(String queryString, Object... values)
  • List findByCriteria(DetachedCriteria criteria)
  • List findByNamedQuery(String queryName, Object... values)

新建包:cn.itcast.ssh.dao,用来编写dao:

第一步:创建IBookDao接口

public interface IBookDao {

 

    //保存图书

    public void save(Book book);

    //更新图书(根据id)

    public void update(Book book);

    //删除图书(根据id)

    public void delete(Book book);

    

    //根据id查询

    public Book findById(Integer id);

    

    //查询所有

    public List<Book> findAll();

    

    //复杂条件条件

    //1.命名查询:QBN

    public List<Book> findByNamedQuery(String queryName, Object... values);

    

    //2.离线条件查询:QBC

    public List<Book> findByCriteria(DetachedCriteria criteria);

}

第二步:创建BookDaoImpl的实现类

使用HibernateTemplate 实现增删改查

//图书的持久层

//HibernateDaoSupport用来简化代码,能提供HibernateTemplate,

public class BookDaoImpl extends HibernateDaoSupport implements IBookDao{

    

    //保存图书

    public void save(Book book){

        //注入sesson工厂,获取session--不会写了

        //因为:spring提供了模版类,来整合Hibernate

        super.getHibernateTemplate().save(book);

    }

    //更新图书(根据id)

    public void update(Book book){

        super.getHibernateTemplate().update(book);

    }

    //删除图书(根据id)

    public void delete(Book book){

        super.getHibernateTemplate().delete(book);

    }

    

    //根据id查询

    public Book findById(Integer id){

        return super.getHibernateTemplate().get(Book.class, id);

    }

    

    //查询所有

    public List<Book> findAll(){

        return super.getHibernateTemplate().loadAll(Book.class);

//        return super.getHibernateTemplate().find("from Book");//hql方式

    }

    

    //复杂条件条件

    //1.命名查询:QBN

    public List<Book> findByNamedQuery(String queryName, Object... values){

        return super.getHibernateTemplate().findByNamedQuery(queryName, values);

    }

    

    //2.离线条件查询:QBC

    public List<Book> findByCriteria(DetachedCriteria criteria){

        return super.getHibernateTemplate().findByCriteria(criteria);

    }

    

 

}

查看HibernateDaoSupport类的底层,我们发现,要想创建、使用HibernateTemplate,必须要注入SessionFactory

那么如何注入SessionFactory呢?

第三步:配置applicationContext.xml

<!-- dao -->

    <bean id="bookDao" class="cn.itcast.ssh.dao.BookDaoImpl">

        <!-- 注入sessionFactory,这是在Dao层使用hibernateTemplate的条件,用来操作数据库的crud -->

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

    </bean>

  1. 编写Service (业务层)

新建包cn.itcast.ssh.service:用来存放service

业务层代码,将dao注入到service,可以使用setXxx方法

第一步:创建接口类IBookService.java

public interface IBookService {

 

    //保存图书

    //方法名不能随便写,配置事务save*,事务才能可写

    public void saveBook(Book book);

    

    //查询:复杂条件查询,根据书名模糊查询,配置事务find*,表示查询,事务只读

    public List<Book> findBookListByNameLike(String name);

}

第二步:创建接口类的实现类BookServiceImpl实现接口类

//图书业务层

public class BookServiceImpl implements IBookService {

    //注入dao

    private IBookDao bookDao;

    

    public void setBookDao(IBookDao bookDao) {

        this.bookDao = bookDao;

    }

 

    //保存图书

    //方法名不能随便写,配置事务save*

    public void saveBook(Book book){

        //调用dao

        bookDao.save(book);

    }

    

    //查询:复杂条件查询,根据书名模糊查询

    public List<Book> findBookListByNameLike(String name){

        //1.qbn

//        return bookDao.findByNamedQuery("Book.findBookListByNameLike", "%"+name+"%");

        

        //2.qbc

        DetachedCriteria criteria = DetachedCriteria.forClass(Book.class);//root对象类型

        criteria.add(Restrictions.like("name", "%"+name+"%"));

        return bookDao.findByCriteria(criteria);

    }

 

}

第三步:Book.hbm.xml文件,定义命名查询:使用qbn的方式查询的时候使用

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="cn.itcast.ssh.domain.Book" table="book">

        <id name="id">

            <generator class="native"/>

        </id>

        <property name="name"/>

        <property name="price"/>

    </class>

<!--定义命名查询-->

    <query name="Book.findBookListByNameLike">

        from Book where name like ?

    </query>

</hibernate-mapping>

第四步:配置applicationContext.xml

<!--service -->

    <bean id="bookService" class="cn.itcast.ssh.service.BookServiceImpl">

        <!-- 注入dao -->

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

    </bean>

第五步:声明式事务管理 ,在Service层完成

<!-- 配置声明式事务处理 -->

    <!-- 平台事务管理器的实现 -->

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <!-- 注入sessin工厂 -->

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

    </bean>

    <!-- 配置声明式事务 -->

    <tx:advice id="txAdvice" transaction-manager="transactionManager">

        <!-- 具体事务的属性策略 -->

        <tx:attributes>

            <tx:method name="save*"/>

            <tx:method name="update*"/>

            <tx:method name="delete*"/>

            <tx:method name="find*" read-only="true"/>

        </tx:attributes>

    </tx:advice>

      

    

    <!-- 事务的切入点和切面配置 -->

    <aop:config>

        <!-- 切入点 -->

        <aop:pointcut expression="bean(*Service)" id="txPointcut"/>

        <!-- 切面 -->

        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>

    </aop:config>

第六步:使用SrpingTest.java进行测试:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class SpringTest {

 

    //注入service

    @Autowired

    private IBookService bookService;

    

    @Test

    public void testSave(){

        //保存图书

        Book book = new Book();

        //book.setId(id)

        book.setName("约会专家周星星");

        book.setPrice(998d);

        bookService.saveBook(book);

    }

    

    @Test

    public void testFind(){

        //查询

        List<Book> list = bookService.findBookListByNameLike("会");

        System.out.println(list);

    }

}

  1. 编写Action (表现层)

  1. 编写添加图书页面addbook.jsp访问到Action

第一步:在WebRoot下新建:addbook.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<%@taglib uri="/struts-tags" prefix="s"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>添加图书</title>

</head>

<body>

    <h1>欢迎使用Xxx的图书管理系统</h1>

    <s:form action="book_add" namespace="/" method="post">

        图书的名称:<s:textfield name="name"/><br/>

        图书的价格:<s:textfield name="price"/><br/>

        <s:submit value="保存"/><s:reset value="重置"/>

    </s:form>

</body>

</html>

第二步:新建cn.itcast.web.action包,存放action,编写BookAction,配置add方法完成添加

//图书管理的表现层

public class BookAction extends ActionSupport implements ModelDriven<Book>{

    //数据模型对象

    private Book book=new Book();

    

    public Book getModel() {

        return book;

    }

    

    //成员变量

    private IBookService bookService;

    //激活了自动注入装配spring的bean的机制功能

    //在实例化action之后,自动到spring容器中,寻找xxx的名字的bean,调用setXxx

    public void setBookService(BookServiceImpl bookService){

        this.bookService = bookService;

    }

    

    //业务方法:保存图书

    public String add(){

        System.out.println(book);

        //调用业务层保存...

        bookService.saveBook(book);

        return NONE;

    }

}

第三步:配置struts.xml

<package name="default" namespace="/" extends="struts-default">

        <!-- 图书管理 -->

        <!-- action还是struts自己管理,自动装配bean的机制 -->

        <action name="book_*" class="cn.itcast.ssh.web.action.BookAction" method="{1}">

            

        </action>

</package>

第四步:测试,访问http://localhost:8080/spring3_day03_ssh1/addbook.jsp

发现空指针异常。因为Action类并没有注入Service,Action类的bookService为空。

  1. Struts2整合Spring

思想: 在Action类注入Service

方式一: Action自动装配Service --(机制:struts整合了spring)

    1) 引入struts2-spring-plugin.jar 插件包

    阅读 插件包根目录 struts-plugin.xml

打开struts-plugin.xml文件

    修改struts的对象工厂为 spring (StrutsSpringObjectFactory),开启struts2整合spring

    

2) struts2-core-2.3.15.3.jar 中default.properties,默认struts2整合spring是注释的,要想使用struts2整合spring需要开启。

    如果对象工厂变为spring之后, autoWire(自动绑定)机制被激活,默认按名称自动注入 bean,即Acton由struts2的一个工厂来生成,并可以自动根据名称来注入spring的bean.(默认,也可以更改为按照类型等。)

简单的再说:你只要更改了创建action对象的工厂是spring(StrutsSpringObjectFactory),那么你的action就可以自动根据名字(setter方法的名字)拿到spring中的bean对象。

    3) Action提供 setXxxService 的方法 ,只要名称和spring中Service匹配,Service就可以自动装配到Action中

例如:在cn.itcast.ssh.web.action中编辑BookAction.java

//图书管理的表现层

public class BookAction extends ActionSupport implements ModelDriven<Book>{

    //数据模型对象

    private Book book=new Book();

    

    public Book getModel() {

        return book;

    }

    

    //成员变量

    private IBookService bookService;

    //激活了自动注入装配spring的bean的机制功能

    //在实例化action之后,自动到spring容器中,寻找xxx的名字的bean,调用setXxx

    public void setBookService(IBookService bookService){

        this.bookService = bookService;

    }

    

    //业务方法:保存图书

    public String add(){

        System.out.println(book);

        //调用业务层保存...

        bookService.saveBook(book);

        return NONE;

    }

}

最后,使用addbook.jsp进行测试

方式二: Action交给Spring容器管理 (推荐 )

    方式一的缺点:Action对象由struts2框架创建, Service根据名称自动装配Action (缺点: Action不能被Spring管理 , 无法对Action使用AOP、bean注入 )

1) 导入struts2-spring-plugin.jar 插件

    对象工厂被修改成spring

    原理:当struts2容器去创建Action对象时, 会根据Action中配置的class(伪类名)名字,优先获取spring容器寻找同名的bean ,找不到再自己构建。

  1. spring配置中配置伪类对应Bean,配置applicationContext.xml

    <!-- action

        scope="prototype":必须是多例,否则会出现线程问题,因为struts2是多线程多实例

         -->

        <bean id="bookAction" class="cn.itcast.ssh.web.action.BookAction" scope="prototype">

            <!-- 注入dao -->

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

        </bean>

  2. struts2 配置Action的class 是一个伪类

    <package name="default" namespace="/" extends="struts-default">

            <!-- 图书管理 -->

            <!-- action还是struts自己管理,自动装配bean的机制 -->

              

         <!-- struts2全权交给spring装配管理

                当StrutsSpringObjectFactory的来创建action,struts2会拿class的名字在spring容器中找,同名的bean,如果有,则直接拿过来用,此时不需要在创建。

                如果没有,则自己想办法new

                即:

                class="bookAction",使用bookAction到spring的容器applicationContext.xml中查找对象

                <bean id="bookAction" class="cn.itcast.ssh.web.action.BookAction" scope="prototype">

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

                </bean>

            -->

            <action name="book_*" class="bookAction" method="{1}">

                

            </action>

    </package>

    【注意】

        面试问题:Struts2管理Action 和 Spring 管理Action 区别?

        (1)Struts2 管理Action,由struts来构建action, 默认多实例

    <package name="default" namespace="/" extends="struts-default">

            <!-- 图书管理 -->

            <!-- action还是struts自己管理,自动装配bean的机制 -->

              

         <!-- struts2全权交给spring装配管理

                当StrutsSpringObjectFactory的来创建action,struts2会拿class的名字在spring容器中找,同名的bean,如果有,则直接拿过来用,此时不需要在创建。

                如果没有,则自己想办法new

                即:

                class="bookAction",使用bookAction到spring的容器applicationContext.xml中查找对象

                <bean id="bookAction" class="cn.itcast.ssh.web.action.BookAction" scope="prototype">

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

                </bean>

            -->

            <action name="book_*" class="cn.itcast.ssh.web.action.BookAction" method="{1}">

                

            </action>

    </package>

        (2)Spring 管理Action,action由spring来创建,Action 默认singleton单例 (引发问题 ),所以需要配置 scope="prototype"

    <!-- action

        scope="prototype":必须是多例,否则会出现线程问题,因为struts2是多线程多实例

         -->

        <bean id="bookAction" class="cn.itcast.ssh.web.action.BookAction" scope="prototype">

            <!-- 注入dao -->

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

        </bean>

    (3)都需要导入struts2-spring-plugin.jar

    ssh整合小结:

  • Spring和hibernate 通过管理SessionFactory (LocalSessionFactoryBean)

* 方式一:加载hibernate自己的配置文件,使用hibernate.cfg.xml

* 方式二:将所有参数配置到spring,不需要hibernate配置文件,省略掉hibernate.cfg.xml ,hibernateTemplate的使用(dao继承hibernateDaoSupport)。

  • Spring和Struts2 的整合:通过修改对象工厂,放入struts2-spring-plugin.jar 插件包

* 方式一: struts.xml配置Action全名,struts2管理Action对象,自动装配Service对象,

* 方式二: struts.xml配置伪类名, 对应spring中Action Bean, spring管理Action, 配置注入Service

  1. 延迟加载问题 OpenSessionInView方案

什么是延迟加载问题 ?

业务层查询数据,返回后,session关闭了, 表现层获取数据如果关联延迟数据,无法初始化 ! (No Session 延迟加载问题 )

问题: 如何解决延迟加载问题 ?

     方案一: 在Xxx.hbm.xml中配置为立即加载 lazy=false (不推荐 )

     方案二: Service方法返回前, 对延迟数据进行初始化 (缺点多写代码 )

List<Employee> list = dao.findAll ();

for(Employee e : list ){

    Hibernate.initialize(e.getDepartment() );

}

     方案三: spring提供了OpenSessionInView 机制 (将Session开启到表现层 最前面 Filter )

Spring 提供 OpenSessionInViewFilter [注意:需要配置在struts2 Filter前面,否则不起作用 ]

web.xml

    <!-- OpenSessionInView机制:会将会话到表现层,让会话在请求结束之后关闭,延迟了session关闭,需要放置到struts2的过滤器的前面 -->

    <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>

OpenSessionInViewFilter:在request过程中维持session。延迟session的关闭,直到request结束,再自动关闭session,也就是说,直到表现层的数据全部加载完毕,再关闭Session。

注意(副作用): 如果没有被事务管理的方法, OpenSessionInViewFilter 会将这些方法的事务变为 readOnly 的 !

例如:addBook方法没有添加事务,按道理它是可写的方法,但是这里只会readOnly只读的。

懒加载异常:

解决方案:必须将事务拦截到的方法,都配置事务的属性,如果不配置会默认成readOnly只读操作。

  1. SSH整合(注解方式)

    1. 导入jar包和配置

第一步:新建 web工程 spring3_day03_sshannotation

  • Jar包 导入 SSH整合 37个jar包 + struts2-convention-plugin.jar (约定扫描)共38个
  • 配置文件

src下log4j.properties 日志配置

src下创建db.properties文件,配置数据库的连接

jdbc.driverClass=com.mysql.jdbc.Driver

jdbc.url jdbc:mysql:///itcastspring

jdbc.username = root

jdbc.password = root

src下 applicationContext.xml 整合Hibernate (配置SessionFactory )

<!-- 引入外部属性配置文件 -->

    <context:property-placeholder location="classpath:db.properties"/>

    <!-- c3p0连接池 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

        <property name="driverClass" value="${jdbc.driverClass}"/>

        <property name="jdbcUrl" value="${jdbc.url}"/>

        <property name="user" value="${jdbc.username}"/>

        <property name="password" value="${jdbc.password}"/>

    </bean>

  1. Hibernate 实体类 映射采用JPA的注解完成

@Entity 实体

@Table 表映射

@Id 主键标识

@GeneratedValue JPA生成策略

@GeneratedGenerator 指定hibernate生成策略

@Column 普通列映射

@OneToMany @ManyToOne @ManyToMany @OneToOne 、@JoinColumn @JoinTable 关系

@Cascade 配置hibernate级联

@Fetch @LazyToOne @LazyCollection 配置抓取策略

@Cache 配置二级缓存

@NamedQueries @NamedQuery 配置命名查询 HQL

@NamedNaviveQueries @NamedNaviteQuery 配置命名查询 SQL

注解配置文档,可以参考:hibernate注解说明文档.pdf

第一步:在cn.itcast.ssh.domain包下创建Book.java对象,添加注解的方式,取代Book.hbm.xml文件

//po实体类

@Entity

@Table(name="book")

@NamedQuery(name="Book.findBookListByNameLike",query="from Book where name like ?")

public class Book {

    @Id        

    @GeneratedValue(strategy=GenerationType.AUTO)//自动

    private Integer id;//oid

    private String name;

    private Double price;

    

    public Integer getId() {

        return id;

    }

    public void setId(Integer id) {

        this.id = id;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public Double getPrice() {

        return price;

    }

    public void setPrice(Double price) {

        this.price = price;

    }

    

    public String toString() {

        return "Book [id=" + id + ", name=" + name + ", price=" + price + "]";

    }

      

    

 

}

  1. spring整合hibernate配置sessionFactory

第一步:使用AnnotationSessionFactoryBean 配置SessionFactory !

<!-- hibernate的会话工厂 -->

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

        <!-- 1.数据源 -->

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

        <!-- 2.Hibernate的属性 -->

        <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="hibernate.hbm2ddl.auto">update</prop>

            </props>

        </property>

        <!-- 3.注解实体扫描 -->

        <property name="packagesToScan">

        <!-- 扫描某包中的所有@entity注解实体 -->

            <list>

                <value>cn.itcast.ssh.domain</value>

            </list>

        </property>

    </bean>

第二步:在web.xml 注册struts2 (Filter器) 和 spring (Listener器 )

<!-- struts2的前端控制器 -->

    <filter>

        <filter-name>struts2</filter-name>

        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name>struts2</filter-name>

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

    </filter-mapping>

    <!-- spring的核心监听器:初始化spring容器 -->

    <listener>

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

    </listener>

    <context-param>

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

        <param-value>classpath:applicationContext.xml</param-value>

    </context-param>

第三步:测试,将项目部署到tomcat,删除数据表,启动项目如果能自动建表 ---- 整合成功 !

  1. 编写Action、Service、Dao注解注册<bean>

    1. 编写BookDao

第一步:配置applicationContext.xml,配置组件扫描Bean,开启注解功能

<!-- bean组件扫描配置,开启注解功能,可以在类上使用@Repository,@Service,@Action,@Component -->

    <context:component-scan base-package="cn.itcast.ssh"/>

新建包:cn.itcast.ssh.dao,用来编写dao:

第二步:创建IBookDao接口

public interface IBookDao {

 

    //保存图书

    public void save(Book book);

    //更新图书(根据id)

    public void update(Book book);

    //删除图书(根据id)

    public void delete(Book book);

    

    //根据id查询

    public Book findById(Integer id);

    

    //查询所有

    public List<Book> findAll();

    

    //复杂条件条件

    //1.命名查询:QBN

    public List<Book> findByNamedQuery(String queryName, Object... values);

    

    //2.离线条件查询:QBC

    public List<Book> findByCriteria(DetachedCriteria criteria);

}

第三步:创建BookDaoImpl的实现类

使用HibernateTemplate 实现增删改查,使用注解@Repository和@Autowired

//图书的持久层

//HibernateDaoSupport用来简化代码,能提供HibernateTemplate,

@Repository("bookDao")

public class BookDaoImpl extends HibernateDaoSupport implements IBookDao {

    //注入sessionFactory:注意写法,这样才可以使用hibernateTemplate

    @Autowired

    public void setSuperSessionFactory(SessionFactory sessionFactory){

        super.setSessionFactory(sessionFactory);

    }

    

    //保存图书

    public void save(Book book){

        //注入sesson工厂,获取session--不会写了

        //因为:spring提供了模版类,来整合Hibernate

        super.getHibernateTemplate().save(book);

    }

    //更新图书(根据id)

    public void update(Book book){

        super.getHibernateTemplate().update(book);

    }

    //删除图书(根据id)

    public void delete(Book book){

        super.getHibernateTemplate().delete(book);

    }

    

    //根据id查询

    public Book findById(Integer id){

        return super.getHibernateTemplate().get(Book.class, id);

    }

    

    //查询所有

    public List<Book> findAll(){

        return super.getHibernateTemplate().loadAll(Book.class);

//            return super.getHibernateTemplate().find("from Book");//hql方式

    }

    

    //复杂条件条件

    //1.命名查询:QBN

    public List<Book> findByNamedQuery(String queryName, Object... values){

        return super.getHibernateTemplate().findByNamedQuery(queryName, values);

    }

    

    //2.离线条件查询:QBC

    public List<Book> findByCriteria(DetachedCriteria criteria){

        return super.getHibernateTemplate().findByCriteria(criteria);

    }

 

}

查看HibernateDaoSupport类的底层,我们发现,要想创建、使用HibernateTemplate,必须要注入SessionFactory

那么如何注入SessionFactory呢?使用@Autowired

//注入sessionFactory:注意写法,这样才可以使用hibernateTemplate

    @Autowired

    public void setSuperSessionFactory(SessionFactory sessionFactory){

        super.setSessionFactory(sessionFactory);

    }

  1. 编写BookService(业务层)

新建包cn.itcast.ssh.service:用来存放service

业务层代码,将dao注入到service,可以使用@Autowired存放到属性上

第一步:创建接口类IBookService.java

public interface IBookService {

 

    //保存图书

    //方法名不能随便写,配置事务save*,事务才能可写

    public void saveBook(Book book);

    

    //查询:复杂条件查询,根据书名模糊查询,配置事务find*,表示查询,事务只读

    public List<Book> findBookListByNameLike(String name);

}

第二步:创建接口类的实现类BookServiceImpl实现接口类

//图书业务层

@Service("bookService")

@Transactional(readOnly=true)//事务(类级别的事务,一般定义成只读,方法级别的事务定义成可写)

public class BookServiceImpl implements IBookService{

    //注入dao

    @Autowired

    private IBookDao bookDao;

 

    //保存图书

    @Transactional(readOnly=false)//事务(方法级别的事务,覆盖类级别的事务)

    public void saveBook(Book book){

        //调用dao

        bookDao.save(book);

    }

    

    //查询:复杂条件查询,根据书名模糊查询

    public List<Book> findBookListByNameLike(String name){

        //1.qbn

//        return bookDao.findByNamedQuery("Book.findBookListByNameLike", "%"+name+"%");

        

        //2.qbc

        DetachedCriteria criteria =DetachedCriteria.forClass(Book.class);//root对象类型

        criteria.add(Restrictions.like("name", "%"+name+"%"));

        return bookDao.findByCriteria(criteria);

    }

 

}

第三步:声明式事务管理 ,在Service层完成,添加注解@Transactional

  1. 在applicationContext.xml文件中配置事务控制的注解写法

    <!-- 平台事务管理器 -->

        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

            <!-- 注入sessionFactory -->

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

        </bean>

        <!-- 声明式事务(注解驱动) -->

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

    (2)类上定义只读,因为一个类中查询的方法比较多

    @Transactional(readOnly=true)//事务(类级别的事务,一般定义成只读,方法级别的事务定义成可写)

    public class BookServiceImpl implements IBookService{

     

    }

    (3)增删改方法上定义可写,可写的操作需要将事务定义可写

    //保存图书

    @Transactional(readOnly=false)//事务(方法级别的事务,覆盖类级别的事务)

    public void saveBook(Book book){

     

    }

    第四步:使用SrpingTest.java进行测试:

    @RunWith(SpringJUnit4ClassRunner.class)

    @ContextConfiguration(locations="classpath:applicationContext.xml")

    public class SpringTest {

     

        //注入service

        @Autowired

        private IBookService bookService;

        

        @Test

        public void testSave(){

            //保存图书

            Book book = new Book();

            //book.setId(id)

            book.setName("约会专家周星星");

            book.setPrice(998d);

            bookService.saveBook(book);

        }

        

        @Test

        public void testFind(){

            //查询

            List<Book> list = bookService.findBookListByNameLike("会");

            System.out.println(list);

        }

    }

    1. 编写BookAction

    在cn.itcast.ssh.web.action中创建BookAction类,注解完成Service注入Action

    //图书管理的表现层

    @Controller("bookAction")

    @Scope("prototype")//多例!!!!!

    public class BookAction extends ActionSupport implements ModelDriven<Book>{

        //数据模型对象

        private Book book=new Book();

        

        public Book getModel() {

            return book;

        }

        

        //成员变量

        @Autowired

        private IBookService bookService;

        

        //业务方法:保存图书

        public String add(){

            System.out.println(book);

            //调用业务层保存...

            bookService.saveBook(book);

            return NONE;

        }

        

     

    }

    1. 使用struts2注解,页面访问Action

    第一步:在WebRoot下新建:addbook.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

    <%@taglib uri="/struts-tags" prefix="s"%>

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

    <html>

    <head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <title>添加图书</title>

    </head>

    <body>

        <h1>欢迎使用Xxx的图书管理系统</h1>

        <s:form action="book_add" namespace="/" method="post">

            图书的名称:<s:textfield name="name"/><br/>

            图书的价格:<s:textfield name="price"/><br/>

            <s:submit value="保存"/><s:reset value="重置"/>

        </s:form>

    </body>

    </html>

    如果添加namespace:

    <s:form action="book_add" namespace="/ann" method="post"><!--或者是action="/ann/book_add" namespace="/"-->

            图书的名称:<s:textfield name="name"/><br/>

            图书的价格:<s:textfield name="price"/><br/>

            <s:submit value="保存"/><s:reset value="重置"/>

        </s:form>

    第二步:导入Struts2 注解开发,基于约定扫描 (导入 struts2-convention-plugin.jar ),从struts2的struts-2.3.15.3-allstruts-2.3.15.3lib查找

    找到struts-plugin.xml文件

    打开文件分析:

    1、<constant name="struts.convention.package.locators" value="action,actions,struts,struts2"/>

    表示:用来配置,哪些包会被扫描 ------ 使用注解的Action必须位于四个包和子包中 !

    2、<constant name="struts.convention.action.suffix" value="Action"/>

    表示:扫描Action 结尾的类

    3、应用struts2 注解

        @Action 访问

        @Result 结果集

        @NameSpace 包名称空间 默认 /

        @ParentPackage 配置父包 默认 struts-default

    第三步:配置struts.xml文件

    <?xml version="1.0" encoding="UTF-8" ?>

    <!DOCTYPE struts PUBLIC

        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

        "http://struts.apache.org/dtds/struts-2.3.dtd">

    <struts>

        <!-- 开发者模式:关闭:副作用:如果是ajax请求的情况下,一旦出错,不会在页面上打印错误 -->

    <!-- <constant name="struts.devMode" value="true" /> -->

     

    <!-- 如果使用ajax调试,可以使用:自动加载核心配置文件 -->

        <constant name="struts.configuration.xml.reload" value="true"/>

        <!-- 如果使用ajax调试,可以使用:自动加载国际化资源文件 -->

    <constant name="struts.i18n.reload" value="true"/>

        <!-- 主题样式 -->

        <constant name="struts.ui.theme" value="simple"/>

     

     

    </struts>

    在cn.itcast.ssh.web.action中创建BookAction的完整代码:

    package cn.itcast.ssh.web.action;

     

    import org.apache.struts2.convention.annotation.Action;

    import org.apache.struts2.convention.annotation.Namespace;

    import org.apache.struts2.convention.annotation.ParentPackage;

    import org.apache.struts2.convention.annotation.Result;

    import org.apache.struts2.convention.annotation.Results;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.context.annotation.Scope;

    import org.springframework.stereotype.Controller;

     

    import cn.itcast.ssh.domain.Book;

    import cn.itcast.ssh.service.IBookService;

     

    import com.opensymphony.xwork2.ActionSupport;

    import com.opensymphony.xwork2.ModelDriven;

    //图书管理的表现层

    @Controller("bookAction")

    @Scope("prototype")//多例!!!!!

    @ParentPackage("struts-default")

    @Namespace("/ann")

    @Results({@Result(name="success",location="/index.jsp")})//全局的结果集

    public class BookAction extends ActionSupport implements ModelDriven<Book>{

        //数据模型对象

        private Book book=new Book();

        

        public Book getModel() {

            return book;

        }

        

        //成员变量

        @Autowired

        private IBookService bookService;

        

        //业务方法:保存图书

        //业务方法:保存图书

        @Action(value="book_add"

                ,results={@Result(name="success",location="/index.jsp")})

        public String add(){

            System.out.println(book);

            //调用业务层保存...

            bookService.saveBook(book);

            return "success";

        }

    }

    1. Spring和Ehcache缓存集成

    缓存作用:提升查询的效率,降低数据库的压力。

    Hibernate的二级缓存和Spring缓存的对比:

    Hibernate的二级缓存缺点:使用麻烦,只能缓存hibernate相关的对象。

    spring的缓存:非常灵活,缓存任何的对象。使用简单。

    Spring的缓存:可以整合第三方缓存框架,比如ehcache。


    spring的缓存使用原理:基于aop面向切面的编程。

     

    开发步骤:

  • 确定目标(要缓存哪些bean的哪些方法)
  • 编写通知,配置通知
  • 配置aop:切入点和切面

 

缓存从xml方式+注解方式分别讲解。

 

第一种方式:xml方式:

spring缓存的配置原理:(xml)

开发步骤:

1.确定目标:BookServiceImpl类中的查询方法

2.编写通知(spring帮你写好了),只需要配置,使用标签<cache:advice>

第一步:导入jar包

(1)spring-context-support-3.2.0.RELEASE.jar包

提供了平台缓存管理器相关class。

引入:

spring-framework-3.0.2.RELEASE-dependencies et.sourceforge.ehcachecom.springsource.net.sf.ehcache1.6.2

com.springsource.net.sf.ehcache-1.6.2.jar

 

第二步:引入约束

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

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

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

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

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

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

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

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

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

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

配置本地提示:导入spring-cache-3.2.xsd文件。

 

第三步:配置ehcache的缓存区域:从com.springsource.net.sf.ehcache-1.6.2.jar包中拷贝ehcache-failsafe.xml文件的内容

在src下创建ehcache.xml文件,用来覆盖ehcache-failsafe.xml文件,ehcache.xml文件的内容如下:

 

属性说明参考: http://blog.163.com/zsq303288862@126/blog/static/937459612011116112640694/

 

第四步:配置applicationContext.xml文件

<!-- 使用Xml的方式配置缓存 -->

    <!--

        第一步:配置缓存管理器工厂

     -->

    <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">

        <!--

            注入ehcache的核心配置文件

            classpath中寻找ehcache.xml

        -->

        <property name="configLocation" value="classpath:ehcache.xml"/>

    </bean>

    

    <!-- 第二步:具体平台缓存管理器:整合ehcache的实现,需要导入jar -->

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">

        <!-- 注入ehcache的缓存工厂 -->

        <property name="cacheManager" ref="ehCacheManager"/>

    </bean>

    

    <!--

    第三步:配置spring的缓存通知

        * id:通知的名字

        * cache-manager:缓存管理器,spring要整合缓存,这里要提供具体的缓存的方案的平台缓存管理器

     -->

    <cache:advice id="cacheAdvice" cache-manager="cacheManager">

        <!-- 缓存的具体策略

        cache:ehcache.xml文件中的缓存区域的名称

         -->

        <cache:caching cache="bookCache">

            <!-- 拦截到的方法中,哪些方法,的结果要放入缓存 -->

            <cache:cacheable method="findBookByNameLike "/>

            <!-- 拦截到的方法中,哪些方法,清除缓存 -->

            <cache:cache-evict method="save*" all-entries="true"/>

            <cache:cache-evict method="update*" all-entries="true"/>

            <cache:cache-evict method="delete*" all-entries="true"/>

        </cache:caching>

    </cache:advice>

    

    <!-- 第四步:缓存的切面和切入点 -->

    <aop:config>

        <!-- 切入点 -->

        <aop:pointcut expression="bean(*Service)" id="cachePointcut"/>

        <!-- 切面,通知的方法关联切入点 -->

        <aop:advisor advice-ref="cacheAdvice" pointcut-ref="cachePointcut"/>

    </aop:config>

 

 

第五步:测试代码:在cn.itcast.ssh.test包中创建BookServiceTest进行测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class BookServiceTest {

    //注入service

    @Autowired

    private IBookService bookService;

 

    @Test

    public void testSaveBook() {

        Book book = new Book();

        book.setName("葵花宝典233");

        book.setPrice(998d);

        bookService.saveBook(book);

    }

 

    @Test

    public void testFindBookListByNameLike() {

        List<Book> list = bookService.findBookListByNameLike("星");

        System.out.println(list);

        List<Book> list2 = bookService.findBookListByNameLike("星");

        System.out.println(list2);

        Book book = new Book();

        book.setName("葵花宝典2");

        book.setPrice(998d);

        bookService.saveBook(book);

        List<Book> list3 = bookService.findBookListByNameLike("星");

        System.out.println(list3);

        List<Book> list4 = bookService.findBookListByNameLike("星");

        System.out.println(list4);

    }

 

}

查看测试结果,控制台中输出:

第一次查询数据库,第二次从缓存中查询!

 

第二种方式:注解方式:

注解的方式使用缓存:

第一步:导入jar包

(1)spring-context-support-3.2.0.RELEASE.jar包

提供了平台缓存管理器相关class。

引入:

第二步:引入ehcache.xml文件:

 

 

第三步:引入头信息(和上面一样)

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

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

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

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

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

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

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

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

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

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

 

第四步:编写applicationContext.xml,使用注解方式:

<!-- 使用注解的方式配置缓存 -->

    <!-- 第一步:定义ehcache的对象EhCacheManager:spring提供了工厂,专业来new对象 -->

    <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">

        <!-- 注入ehcache的核心配置文件

        通过源代码发现,这里即使不配置,默认就到classpath中寻找ehcache.xml

         -->

        <property name="configLocation" value="classpath:ehcache.xml"/>

    </bean>

    <!-- 第二步:具体平台缓存管理器:整合ehcache的实现,需要导入jar -->

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">

        <!-- 注入ehcache的缓存对象 -->

        <property name="cacheManager" ref="ehCacheManager"/>

    </bean>

    <!-- 第三步:配置缓存注解驱动 -->

    <cache:annotation-driven cache-manager="cacheManager"/>

第五步:使用缓存的bean的类型的方法上,加两个注解:

在cn.itcast.ssh.service包中的类BookServiceImpl.java中添加缓存的注解

//图书业务层

@Service("bookService")

@Transactional(readOnly=true)//事务(类级别的事务,一般定义成只读,方法级别的事务定义成可写)

public class BookServiceImpl implements IBookService{

    //注入dao

    @Autowired

    private IBookDao bookDao;

 

    //保存图书

    @Transactional(readOnly=false)//事务(方法级别的事务,覆盖类级别的事务)

    @CacheEvict(value="bookCache",allEntries=true)

    public void saveBook(Book book){

        //调用dao

        bookDao.save(book);

    }

    

    //查询:复杂条件查询,根据书名模糊查询

    @Cacheable(value="bookCache")//value:echache缓存区域的名字

    public List<Book> findBookListByNameLike(String name){

        //1.qbn

        return bookDao.findByNamedQuery("Book.findBookListByNameLike", "%"+name+"%");

        

        //2.qbc

//        DetachedCriteria criteria =DetachedCriteria.forClass(Book.class);//root对象类型

//        criteria.add(Restrictions.like("name", "%"+name+"%"));

//        return bookDao.findByCriteria(criteria);

    }

 

}

 

第六步:测试代码:在cn.itcast.ssh.test包中创建BookServiceTest进行测试

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations="classpath:applicationContext.xml")

public class BookServiceTest {

    //注入service

    @Autowired

    private IBookService bookService;

 

    @Test

    public void testSaveBook() {

        Book book = new Book();

        book.setName("葵花宝典233");

        book.setPrice(998d);

        bookService.saveBook(book);

    }

 

    @Test

    public void testFindBookListByNameLike() {

        List<Book> list = bookService.findBookListByNameLike("星");

        System.out.println(list);

        List<Book> list2 = bookService.findBookListByNameLike("星");

        System.out.println(list2);

        Book book = new Book();

        book.setName("葵花宝典2");

        book.setPrice(998d);

        bookService.saveBook(book);

        List<Book> list3 = bookService.findBookListByNameLike("星");

        System.out.println(list3);

        List<Book> list4 = bookService.findBookListByNameLike("星");

        System.out.println(list4);

    }

 

}

查看测试结果,控制台中输出:

第一次查询数据库,第二次从缓存中查询!

 

小结:推荐是用注解,简单,xml配置麻烦。

 

 

作业:

  1. xml整合 (两遍 , 背着写一遍 )
  2. 声明式事务管理原理 (转账)(xml和注解)
  3. 注解整合(练习一遍)
  4. spring的缓存整合配置一下,跑跑示例。--会配置,会用。

 

 

 

原文地址:https://www.cnblogs.com/beyondcj/p/6271051.html