zoukankan      html  css  js  c++  java
  • 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的缓存整合配置一下,跑跑示例。--会配置,会用。

     

     

     

  • 相关阅读:
    学习Hadoop不错的系列文章(转)
    浏览器的渲染原理简介
    大数据人才缺乏,你准备好了吗?
    SVN分支与合并透析
    Windows下SVN服务端(Subversion)及客户端(TortoiseSVN)详细安装教程
    maven2介绍(转)
    eclipse安装velocity插件(转)
    为大数据时代做好准备——来自《大数据的冲击》一书精彩片段(转)
    【VB】Format 格式化日期时间数字函数详解
    获取本机ID和电脑名称
  • 原文地址:https://www.cnblogs.com/beyondcj/p/6271051.html
Copyright © 2011-2022 走看看