Spring 事务处理、整合web

录:

1、回顾事务
2、spring事务管理
3、事务案例--转账
4、手动管理事务(了解)
5、工厂bean 生成代理:半自动(了解)
6、AOP配置事务--基于xml【掌握】
7、AOP配置事务--基于注解【掌握】
8、整合web--在web.xml中配置spring容器
9、从Servlet中获取spring容器
10、SSH整合--jar包
11、spring整合hibernate:没有hibernate.cfg.xml 【重点】
12、spring整合struts:struts创建action 【重点】

1、回顾事务    <--返回目录

    * 事务:一组业务操作ABCD,要么全部成功,要么全部不成功。
    * 特性:ACID
        - 原子性:整体
        - 一致性:完成
        - 隔离性:并发
        - 持久性:结果
    * 隔离问题:
        - 脏读:一个事务读到另一个事务没有提交的数据
        - 不可重复读:一个事务读到另一个事务已提交的数据(update)
        - 虚读(幻读):一个事务读到另一个事务已提交的数据(insert)
    * 隔离级别:
        - read uncommitted:读未提交。存在3个问题
        - read committed:读已提交。解决脏读,存在2个问题
        - repeatable read:可重复读。解决:脏读、不可重复读,存在1个问题。
        - serializable :串行化。都解决,单事务。
    * mysql 事务操作

ABCD 一个事务
Connection conn = null;
try{
  //1 获得连接
  conn = ...;
  //2 开启事务
  conn.setAutoCommit(false);
  A
  B
  C
  D
  //3 提交事务
  conn.commit();
} catche(){
  //4 回滚事务
  conn.rollback();
}

    * mysql 事务操作--Savepoint

需求:AB(必须),CD(可选) 
Connection conn = null;
Savepoint savepoint = null;  //保存点,记录操作的当前位置,之后可以回滚到指定的位置。(可以回滚一部分)
try{
  //1 获得连接
  conn = ...;
  //2 开启事务
  conn.setAutoCommit(false);
  A
  B
  savepoint = conn.setSavepoint();
  C
  D
  //3 提交事务
  conn.commit();
} catche(){
  if(savepoint != null){   //CD异常
     // 回滚到CD之前
     conn.rollback(savepoint);
     // 提交AB
     conn.commit();
  } else{   //AB异常
     // 回滚AB
     conn.rollback();
  }
}

2、spring事务管理    <--返回目录

    * 涉及事务,首先需要jar包:spring-tx-3.2.0.RELEASE.jar
    * 三个顶级接口
        - PlatformTransactionManager  平台事务管理器,spring要管理事务,必须使用事务管理器
            进行事务配置时,必须配置事务管理器。
        - TransactionDefinition:事务详情(事务定义、事务属性),spring用于确定事务具体详情,
            例如:隔离级别、是否只读、超时时间 等
            进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。
        - TransactionStatus:事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。
            spring底层根据状态进行相应操作。
    * 以后,我们需要做的:配置事务管理器,配置事务详情,事务状态我们不管
    
    * 常见的事务管理器
        - DataSourceTransactionManager,jdbc开发时事务管理器,采用JdbcTemplate
            ** 需要导spring-jdbc-3.2.0.RELEASE.jar
        - HibernateTransactionManager,hibernate开发时事务管理器,整合hibernate
            ** 需要导spring-orm-3.2.0.RELEASE.jar
            
    * 事务管理器的api详解
        TransactionStatus getTransaction(TransactionDefinition definition):事务管理器 通过"事务详情",
            获得"事务状态",从而管理事务。
        void commit(TransactionStatus status)  根据状态提交
        void rollback(TransactionStatus status) 根据状态回滚
    
    * 事务状态TransactionStatus的方法
        isNewTranction():是否是新的事务
        hasSavepoint():是否有保存点
        setRollbackOnly():设置回滚
        isRollbackOnly():是否回滚
        flush():刷新
        isCompleted():是否完成
        
    * 事务详情TransactionDefinition
        - getName()方法:配置事务详情名称,一般取方法名称,例如save、add*等
        - isReadOnly():是否只读,一般增删改:读写,查询:只读
        - getTimeout():获得超时时间
        - getIsolationLevel():隔离级别
        - getPropagationBehavior():传播行为
        
        - TIMEOUT_DEFAULT:默认超时时间,默认值-1,使用数据库底层的超时时间
        - ISOLATION_DEFAULT:隔离级别 0 1 2 4 8
        - ISOLATION_READ_UNCOMMITTED
        - ISOLATION_READ_COMMITTED
        - ISOLATION_REPEATABLE_READ
        - ISOLATION_SERIALIZABLE
        
        - 传播行为:在两个业务之间如何共享事务  A调用B,设置B的传播行为
            ** PROPAGATION_REQUIRED【默认值】  required 必须:支持当前事务,A有事务,B将使用该事务;
                如果A没有事务,B将创建一个新的事务
            ** PROPAGATION_SUPPORTS supports 支持:支持当前事务,A有事务,B将使用该事务;
                如果A没有事务,B将以非事务执行
            ** PROPAGATION_MANDATORY mandatory 强制:支持当前事务,A有事务,B将使用该事务;
                如果A没有事务,B将抛异常
            
            ** PROPAGATION_REQUIRES_NEW【以后常用】 必须新的:如果A有事务,将A的事务挂起,
                B创建一个新的事务;如果A没有事务,B创建新的事务
                
            ** PROPAGATION_NOT_SUPPORTED 不支持:如果A有事务,将A的事务挂起,B将以非事务执行;
                如果A没有事务,B将以非事务执行
                
            ** PROPAGATION_NEVER 不支持事务,也不支持当前事务:如果A有事务,B将抛异常;
                如果A没有事务,B以非事务执行
            
            ** PROPAGATION_NESTED【也要掌握】 嵌套:A和B底层采用保存点机制,执行嵌套事务

3、事务案例--转账    <--返回目录

  创建表

create database db_test1 default character set utf8;
use db_test1;
create table t_account(
  id int primary key auto_increment,
  username varchar(50),
  money int
)engine innodb;
insert into t_account(username,money) values('jack','1000');
insert into t_account(username,money) values('rose','1000');

  导入jar包

** 核心:4+1
** aop :4 (aop联盟、spring aop、aspectjweaver规范、spring aspect)
** jdbc和事务相关:2(jdbc、tx)
** 驱动:mysql
** 连接池:c3p0

  dao层:接口和实现类

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    @Override
    public void out(String outer, Integer money) {
        this.getJdbcTemplate().update("update account set money = money - ? 
            where username = ?", money,outer);
    }
    @Override
    public void in(String inner, Integer money) {
        this.getJdbcTemplate().update("update account set money = money + ? 
            where username = ?", money,inner);
    }
}

  service层:接口和实现类

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Override
    public void transfer(String outer, String inner, Integer money) {
        accountDao.out(outer, money);  //outer减钱
        //断电,这样就会导致:outer减钱,但是inter不加钱
        //int i = 1/0;
        accountDao.in(inner, money);  //inter加钱
    }
}

  spring配置

<!-- 1 数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ee19_spring_day03"></property>
    <property name="user" value="root"></property>
    <property name="password" value="1234"></property>
</bean>

<!-- 2 dao  依赖JdbcTemplate,继承JdbcDaoSupport,需要注入数据源
-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 3 service 依赖dao层,需要注入accountDao-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"></property>
</bean>

  测试

@Test
public void demo01(){
    String xmlPath = "applicationContext.xml";
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
    AccountService accountService =  (AccountService) applicationContext.getBean
        ("accountService");
    accountService.transfer("jack", "rose", 100);
}

  

4、手动管理事务(了解)    <--返回目录

* spring底层使用TransactionTemplate事务模板进行操作
* 操作
    - service需要获得TransactionTemplate
    - 所以,在spring配置TransactionTemplate,并注入到service
    - 模板TransactionTemplate需要注入事务管理器
    - 所以,需要配置事务管理器DataSourceTransactionManager
    - 事务管理器需要注入DataSource
    1) 配置数据源
    2) 配置事务管理器(依赖数据源)
    3) 配置模板TransactionTemplate(依赖事务管理器)
    4) 配置service(依赖模板TransactionTemplate)

5、工厂bean 生成代理:半自动(了解)    <--返回目录

  spring提供了管理事务的代理工厂bean  TransactionProxyFactoryBean
    1) spring 配置一个代理<bean id="" class="TransactionProxyFactoryBean"/>
    2) getBean(TransactionProxyFactoryBean的id值)获得代理对象

spring配置文件:

<!-- 4 service 代理对象 
4.1 proxyInterfaces 接口 
4.2 target 目标类
4.3 transactionManager 事务管理器
4.4 transactionAttributes 事务属性(事务详情)
    prop.key :确定哪些方法使用当前事务配置
    prop.text:用于配置事务详情
        格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
            传播行为        隔离级别    是否只读        异常回滚        异常提交
        注意:如果这里配置readOnly只读,若执行增删改,会报错
              +Exception:表示有异常,仍提交。可能导致,事务只执行一部分业务
        例如:
            <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> 默认传播行为,
                和隔离级别
            <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop> 只读
            <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,+java.lang.
                ArithmeticException</prop>  有异常仍提交
-->
<bean id="proxyAccountService" class="org.springframework.transaction.interceptor.
    TransactionProxyFactoryBean">
    <property name="proxyInterfaces" value="com.itheima.service.AccountService"></property>指定接口
    <property name="target" ref="accountService"></property>   【指定一个目标类】
    <property name="transactionManager" ref="txManager"></property>  指定事务管理器
    <property name="transactionAttributes">  指定事务详情
        <props>
            <!-- 指定目标类的transfer()方法使用当前事务配置 -->
            <prop key="【目标类方法】transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
        </props>
    </property>
</bean>

<!-- 5 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

  

6、AOP配置事务--基于xml【掌握】    <--返回目录

在spring xml 配置aop 自动生成代理,进行事务的管理,三步
    1)配置管理器
    2)配置事务详情
    3)配置aop

<!-- 4 事务管理 -->
<!-- 4.1 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 4.2 事务详情(事务通知)  , 在aop筛选基础上,对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等
    <tx:attributes> 用于配置事务详情(属性属性)
        <tx:method name=""/> 详情具体配置
            propagation 传播行为 , REQUIRED:必须;REQUIRES_NEW:必须是新的
            isolation 隔离级别
-->
<tx:advice id="txAdvice" transaction-manager="txManager">  
    <tx:attributes>
        <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
    </tx:attributes>
</tx:advice>

<!-- 4.3 AOP编程,目标类有ABCD(4个连接点),切入点表达式 确定增强的连接点,从而获得切入点:ABC -->
<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"/>
</aop:config>

7、AOP配置事务--基于注解【掌握】    <--返回目录

  步骤: 

  1)配置事务管理器,将并事务管理器交予spring
  2)在目标类或目标方法添加注解即可
                ** 在目标类上配,则作用整个类;在目标方法上配,则作用于该方法
                ** @Transactional
                ** @Transactional(propagation=Propagation.REQUIRED , isolation = Isolation.DEFAULT)

  在applicationContext.xml中配置

<!-- 4 事务管理 -->
<!-- 4.1 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 4.2 将管理器交予spring 
    * transaction-manager 配置事务管理器
    * proxy-target-class  true:底层强制使用cglib 代理
-->
<tx:annotation-driven transaction-manager="txManager"/>

8、整合web--在web.xml中配置spring容器    <--返回目录

* 以前测试都是new出spring容器,例如:
        String xmlPath = "applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        AccountService accountService =  (AccountService) applicationContext.getBean("accountService");
        accountService.transfer("jack", "rose", 1000);
    * 但是spring容器创建一次就够了    
        
    * 导入jar包 spring-web
    * 所以,为了让spring配置文件在服务器第一次启动就加载,在web.xml中配置

<!-- 确定spring配置文件位置 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!-- 配置spring监听器,加载xml配置文件 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

  

9、从Servlet中获取spring容器    <--返回目录

* 在Servlet中获取spring容器,从而可以使用
    - Spring把WebApplicationContext(XmlWebApplicationContext是默认实现类)放在了ServletContext中,
        ServletContext也是一个“容器”,也是一个类似Map的结构,而WebApplicationContext在ServletContext中
        的KEY就是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
        我们如果要使用WebApplicationContext则需要从ServletContext取出,Spring提供了一个
        WebApplicationContextUtils类,可以方便的取出WebApplicationContext,
        只要把ServletContext传入就可以了。
    ---------------------
    方法一:
        ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
        ac.getBean("beanId");

    说明:这种方式适用于采用Spring框架的独立应用程序,需要程序通过配置文件手工初始化Spring的情况。

    方法二:通过Spring提供的工具类获取ApplicationContext对象
        ApplicationContext ac1 = WebApplicationContextUtils
                .getRequiredWebApplicationContext(ServletContext sc);
        ApplicationContext ac2 = WebApplicationContextUtils
                .getWebApplicationContext(ServletContext sc);
        ac1.getBean("beanId");
        ac2.getBean("beanId");

    说明:这种方式适合于采用Spring框架的B/S系统,通过ServletContext对象获取ApplicationContext对象,
        然后在通过它获取需要的类实例。
        
    上面两个工具方式的区别是,前者在获取失败时抛出异常,后者返回null。

    其中 servletContext sc 可以具体 换成 servlet.getServletContext()或者 this.getServletContext()
    或者 request.getSession().getServletContext(); 另外,由于spring是注入的对象放在
    ServletContext中的,所以可以直接在ServletContext取出 WebApplicationContext 对象:
    WebApplicationContext webApplicationContext = (WebApplicationContext)servletContext
        .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

    ---------------------
    【总结:通过ServletContext对象获取Spring容器】
    //方式1
    ApplicationContext applicationContext=(ApplicationContext)this.getServletContext()
        .getAtt·ribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    //方式2
    ApplicationContext applicationContext=WebApplicationContextUtils
            .getWebApplicationContext(this.getServletContext());
    //然后在Servlet中可以使用spring容器--applicationContext了

10、SSH整合--jar包    <--返回目录

  最好新建一个文件夹,然后往里面添加jar包,然后添加到项目里
    struts2:2.3.15.3
    hibernate : 3.6.10
    spring: 3.2.0

    * jar包整合
        - struts2:共13个
        - spring
            ** 核心 4+1:beans、core、context、expression,commons-logging (struts已经导入)
            ** aop相关 4个:aop联盟、spring-aop 、aspect规范、spring-aspect
            ** 数据库db 2个:spring-jdbc、spring-tx
            ** 整合jUnit:spring-test
            ** 整合web:spring-web
            ** 整合hibernate:spring-orm
            ** 驱动:mysql
            ** 连接池:c3p0

            共4+4+2+5=15
            
        - hibernate
            ** 核心 1个:hibernate3.jar
            ** 必须:6个
            ** jpa规范 1个
    
            ** 整合log4j  
                导入 log4j...jar (struts已经导入)
                整合(过渡):slf4j-log4j12-1.7.5.jar

            ** 二级缓存
                核心:ehcache-1.5.0.jar
                依赖:backport-util-concurrent-2.1.jar和commons-logging(存在)

            共8+1+2=11    
            
        - spring整合hibernate: spring-orm前面写了
          struts整合spring:struts2-spring-plugin-2.3.15.3.jar

        - 所以,整合ssh共:13+15+11+1=40个 删除一个重复的javassist,最终39个


11、spring整合hibernate:没有hibernate.cfg.xml 【重点】    <--返回目录

    * 与使用jdbcTemplate类似
    * dao层,继承HibernateDaoSupport
        // 底层需要SessionFactory,自动创建HibernateTemplate模板
        public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
            @Override
            public void save(User user) {
                this.getHibernateTemplate().save(user);
            }
        }
    
    * 使用c3p0数据源,数据源4大参数存放到properties文件中
        新建jdbcinfo.properties文件,添加一些内容

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=UTF-8
jdbc.user=root
jdbc.password=

       
    * applicationContext.xml文件配置

<!-- 1.1 加载properties文件 -->
<context:property-placeholder location="classpath:com/itheima/f_properties/jdbcInfo.properties"/>
<!-- 1.2 配置数据源 -->
<bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driverClass}"></property>
    <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    <property name="user" value="${jdbc.user}"></property>
    <property name="password"  value="${jdbc.password}"></property>
</bean>
<!-- 1.3 配置 LocalSessionFactoryBean,获得 ->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.format_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.current_session_context_class">thread</prop>
        </props>
    </property>
    <property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property>
</bean>

<!-- 2 dao,需要注入sessionFactory -->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory"></property>
</bean>

<!-- 3 service -->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>

12、spring整合struts:struts创建action 【重点】    <--返回目录

  配置web.xml

<!-- 1 确定applicationContext.xml位置 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 2 spring监听器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 3 struts 前端控制器 -->
<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>

  要求:Action类中,必须提供service名称与spring配置文件中<bean id="">的id一致。(如果名称一样,将自动注入)

原文地址:https://www.cnblogs.com/xy-ouyang/p/13759316.html