Spring筆記04_AOP注解開發(fā)_模板_事務(wù)

1. Spring基于AspectJ的注解的AOP開發(fā)

1. 1 SpringAOP的注解入門

  • 創(chuàng)建項目确买,導(dǎo)入jar包

    • 需要導(dǎo)入Spring基礎(chǔ)包4+2

    • 需要導(dǎo)入AOP聯(lián)盟包、AspectJ包瞧预、Spring整合Aspect包Spring-aop包

    • Spring整合單元測試包

      image
  • 引入配置文件applicationContext.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: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">
      
    </beans>
    
  • 編寫目標(biāo)類并配置

    package com.itzhouq.spring.demo1;
    
    public class OrderDao {
      public void save() {
          System.out.println("保存訂單谒亦。钞诡。。");
      }
      public void update() {
          System.out.println("修改訂單谷饿。。妈倔。");
      }
      public void find() {
          System.out.println("查找訂單博投。。盯蝴。");
      }
      public String delete() {
          System.out.println("刪除訂單毅哗。。捧挺。");
          return "周杰倫";
      }
    }
    
  • 配置目標(biāo)類虑绵,將目標(biāo)類OrderDao交給Spring管理

    • 在applicationContext.xml中添加
    <!-- 配置目標(biāo)類 -->
      <bean id="orderDao" class="com.itzhouq.spring.demo1.OrderDao"></bean>
    
  • 編寫切面類并配置

    package com.itzhouq.spring.demo1;
    /*
     * 切面類:注解的切面類
     */
    public class MyAspectAnno {
      
      public void before() {
          System.out.println("前置增強===============");
      }
    }
    
    <!-- 配置切面類 -->
      <bean id="myAspect" class="com.itzhouq.spring.demo1.MyAspectAnno"></bean>
    
  • 使用注解的AOP對目標(biāo)類的方法進行增強

    • 首先在配置文件中打開注解的AOP開發(fā)

      <!-- 在配置文件總開啟注解的AOP開發(fā) -->
          <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
      
    • 在切面類上使用注解

      /*
       * 切面類:注解的切面類
       */
      @Aspect //標(biāo)記該類為切面類
      public class MyAspectAnno {
          
          @Before(value="execution(* com.itzhouq.spring.demo1.OrderDao.save(..))")
          public void before() {//這個注解用來標(biāo)記目標(biāo)類的哪個方法使用何種增強
              System.out.println("前置增強===============");
          }
      }
      
  • 測試

    package com.itzhouq.spring.demo1;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /*
     * Spring的AOP注解開發(fā)測試
     */
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class SpringDemo1 {
      @Resource(name="orderDao")  //注入OrderDao
      private OrderDao orderDao;
      
      @Test
      public void test1() {
          orderDao.save();
          orderDao.update();
          orderDao.find();
          orderDao.delete();
    //        前置增強===============
    //        保存訂單。闽烙。蒸殿。
    //        修改訂單。鸣峭。宏所。
    //        查找訂單。摊溶。爬骤。
    //        刪除訂單。莫换。霞玄。
    
      }
    }
    

1.2 Spring的AOP的注解通知類型

1.2.1 @Before:前置通知

1.2.2 @AfterReturning:后置通知

  • 在刪除delete方法上使用后置通知

  • 在切面類中添加以下方法

    //后置通知
      @AfterReturning("execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))")
      public void afterReturning() {
          System.out.println("后置增強==================");
      }
    
  • 不用修改目標(biāo)類直接測試就能實現(xiàn)效果

    前置增強===============
    保存訂單骤铃。。坷剧。
    修改訂單惰爬。。惫企。
    查找訂單撕瞧。。狞尔。
    刪除訂單丛版。。偏序。
    后置增強==================
    
  • 后置通知還可以使用返回值

    //后置通知
      @AfterReturning(value="execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))", returning="result")
      public void afterReturning(Object result) {
          System.out.println("后置增強=================="+result);
      }
    
    前置增強===============
    保存訂單页畦。。研儒。
    修改訂單豫缨。。端朵。
    查找訂單好芭。。逸月。
    刪除訂單栓撞。。碗硬。
    后置增強==================周杰倫
    

1.2.3 環(huán)繞通知

  • 在切面類中添加以下方法:

    //環(huán)繞通知
      @Around(value="execution(* com.itzhouq.spring.demo1.OrderDao.update(..))")
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
          System.out.println("環(huán)繞前增強===========");
          Object obj = joinPoint.proceed();
          System.out.println("環(huán)繞前增強===========");
          return obj;
      }
    
  • 測試

    前置增強===============
    保存訂單瓤湘。。恩尾。
    環(huán)繞前增強===========
    修改訂單弛说。。翰意。
    環(huán)繞前增強===========
    查找訂單木人。。冀偶。
    刪除訂單醒第。。进鸠。
    后置增強==================周杰倫
    

1.2.4 異常拋出通知

  • 異常拋出通知可以獲得異常信息

  • 在切面類中添加方法

      //異常拋出通知
      @AfterThrowing(value="execution(* com.itzhouq.spring.demo1.OrderDao.find(..))",throwing="e")
      public void afterThrowing(Throwable e) {
          System.out.println("異常拋出通知============"+e);
      }
    
  • 在find方法中模擬異常

    public void find() {
          System.out.println("查找訂單稠曼。。客年。");
          int i = 1 / 0;
      }
    
  • 測試

    前置增強===============
    保存訂單霞幅。漠吻。。
    環(huán)繞前增強===========
    修改訂單司恳。途乃。。
    環(huán)繞前增強===========
    查找訂單扔傅。耍共。。
    異常拋出通知============java.lang.ArithmeticException: / by zero
    

1.2.5 最終通知‘

  • 在切面類中添加方法

    //最終通知
      @After(value="execution(* com.itzhouq.spring.demo1.OrderDao.find(..))")
      public void after() {
          System.out.println("最終增強============");
      }
    
  • 測試

    前置增強===============
    保存訂單铅鲤。划提。枫弟。
    環(huán)繞前增強===========
    修改訂單邢享。。淡诗。
    環(huán)繞前增強===========
    查找訂單骇塘。。韩容。
    最終增強============
    異常拋出通知============java.lang.ArithmeticException: / by zero
    

1.3 Spring的AOP的注解的切入點的注解

  • 修改切面類

    package com.itzhouq.spring.demo1;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    
    /*
     * 切面類:注解的切面類
     */
    @Aspect   //標(biāo)記該類為切面類
    public class MyAspectAnno {
      
      @Before(value="MyAspectAnno.pointcut2()")
      public void before() {//這個注解用來標(biāo)記目標(biāo)類的哪個方法使用何種增強
          System.out.println("前置增強===============");
      }
      
      //后置通知
      @AfterReturning(value="execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))", returning="result")
      public void afterReturning(Object result) {
          System.out.println("后置增強=================="+result);
      }
      
      //環(huán)繞通知
      @Around(value="MyAspectAnno.pointcut3()")
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
          System.out.println("環(huán)繞前增強===========");
          Object obj = joinPoint.proceed();
          System.out.println("環(huán)繞前增強===========");
          return obj;
      }
      
      //異常拋出通知
      @AfterThrowing(value="MyAspectAnno.pointcut4()",throwing="e")
      public void afterThrowing(Throwable e) {
          System.out.println("異常拋出通知============"+e);
      }
      
      //最終通知
      @After(value="MyAspectAnno.pointcut1()")
      public void after() {
          System.out.println("最終增強============");
      }
      
      //切入點注解
      @Pointcut(value="execution(* com.itzhouq.spring.demo1.OrderDao.find(..))")
      private void pointcut1() {}
      @Pointcut(value="execution(* com.itzhouq.spring.demo1.OrderDao.save(..))")
      private void pointcut2() {}
      @Pointcut(value="execution(* com.itzhouq.spring.demo1.OrderDao.update(..))")
      private void pointcut3() {}
      @Pointcut(value="execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))")
      private void pointcut4() {}
    }
    
    • 通過切入點的注解款违,可以簡化注解的代碼量
    • 注意:使用類名.pointcut1()時候不要忘記pointcut1()是一個方法,需要帶上()才有效

2. Spring的JDBC的模板的使用

2.1 Spring的JDBC的模板

  • Spring的EE開發(fā)的一站式的框架群凶,有EE開發(fā)中每一層的解決方案插爹。Spring對持久層也提供了解決方案:ORM模塊和JDBC的模塊。

  • Spring提供了很多的模板用于簡化開發(fā)请梢。

    image

2.1.1 JDBC模板使用的入門

  • 創(chuàng)建項目赠尾,引入jar包

    • 引入基本的4+2包

    • 數(shù)據(jù)庫驅(qū)動包

    • Spring的JDBC模板的jar包:事務(wù)管理tx和jdbc的包

    • 單元測試包

      image
  • 創(chuàng)建數(shù)據(jù)庫和表

    create database spring4_day03;
    use spring4_day03;
    create table account(
      id int primary key auto_increment,
      name varchar(20),
      money double
    )
    
  • 使用JDBC模板:保存數(shù)據(jù)

    package com.itzhouq.spring.jdbc.demo1;
    
    import org.junit.Test;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    
    /*
     * JDBC的模板使用
     */
    public class JdbcDemo1 {
      @Test   //JDBC的模板的使用類似于Dbutils
      public void test1() {
          //創(chuàng)建連接池 這里使用Spring默認(rèn)的連接池
          DriverManagerDataSource dataSource = new DriverManagerDataSource();
          dataSource.setDriverClassName("com.mysql.jdbc.Driver");
          dataSource.setUrl("jdbc:mysql:///spring4_day03");
          dataSource.setUsername("root");
          dataSource.setPassword("2626");
          
          //創(chuàng)建jdbc模板
          JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
          jdbcTemplate.update("insert into account values (null, ?,?)", "周杰倫",10000d);
      }
    
    }
    
  • 將日志記錄的配置文件jdbc.properties拷貝到src下

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring4_day03
    jdbc.username=root
    jdbc.password=2626
    
  • 測試能插入數(shù)據(jù)

2.1.2 將連接池和模板交給Spring管理

  • 引入aop的jar包

  • 引入Spring的配置文件applicationContext.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: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">
      
      
    </beans>
    
  • 配置Spring的內(nèi)置連接池,將連接池交給Spring管理

    <!-- 配置Spring的內(nèi)置連接池 -->
      <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          <!-- 屬性注入 ===============-->
          <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
          <property name="url" value="jdbc:mysql:///spring4_day03"></property>
          <property name="username" value="root"></property>
          <property name="password" value="2626"></property>
      </bean>
    
  • 配置Spring的jdbc模板毅弧,將模板交給Spring管理

    <!-- 配置Spring的JDBC模板 =================-->
      <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          <property name="dataSource" ref="dataSource"></property>
      </bean>
    
  • 使用JDBC的模板

    package com.itzhouq.spring.jdbc.demo1;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(value="classpath:applicationContext.xml")
    public class JdbcDemo2 {
      @Resource(name="jdbcTemplate")
      private JdbcTemplate jdbcTemplate;
      
      @Test
      public void test1() {
          jdbcTemplate.update("insert into account values (null, ?,?)", "趙雷",10000d);
      }
    }
    
  • 測試能插入數(shù)據(jù)

2.2 使用開源的數(shù)據(jù)庫連接池

2.2.1 C3P0的使用

  • 導(dǎo)入jar包

    • com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
  • 路徑

    • ..\spring-framework-3.0.2.RELEASE-dependencies\com.mchange.c3p0\com.springsource.com.mchange.v2.c3p0
  • 配置C3P0連接池

    <!-- 配置C3P0連接池 -->
      <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
          <!-- 屬性注入 =============== -->
          <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
          <property name="jdbcUrl" value="jdbc:mysql:///spring4_day03"></property>
          <property name="user" value="root"></property>
          <property name="password" value="2626"></property>
      </bean> 
      <!-- 配置Spring的JDBC模板 =================-->
      <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          <property name="dataSource" ref="dataSource"></property>
      </bean>
    
    • 注意:C3P0連接池的核心類是:com.mchange.v2.c3p0.ComboPooledDataSource
    • 測試能插入數(shù)據(jù)

2.2.2 DBCP的使用

  • 引入jar包

    • com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar
    • com.springsource.org.apache.commons.pool-1.5.3.jar
  • 路徑

    • ..\spring-framework-3.0.2.RELEASE-dependencies\org.apache.commons\com.springsource.org.apache.commons.dbcp
    • ..\spring-framework-3.0.2.RELEASE-dependencies\org.apache.commons\com.springsource.org.apache.commons.pool
  • 配置DBCP的連接池

    <!-- 配置DBCP連接池 -->
      <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
          屬性注入 ===============
          <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
          <property name="url" value="jdbc:mysql:///spring4_day03"></property>
          <property name="username" value="root"></property>
          <property name="password" value="2626"></property>
      </bean>
      
      <!-- 配置Spring的JDBC模板 =================-->
      <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          <property name="dataSource" ref="dataSource"></property>
      </bean>
    
  • 注意:DBCP的核心類為org.apache.commons.dbcp.BasicDataSource气嫁。

  • 測試能插入數(shù)據(jù)

2.3 抽取配置到屬性文件

  • 在src下新建配置文件jdbc.properties

    jdbc.driverClass=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring4_day03
    jdbc.username=root
    jdbc.password=2626
    
  • 在Spring的配置文件中引入屬性文件

    <!-- 引入連接數(shù)據(jù)的屬性文件 -->
      <context:property-placeholder location="classpath:jdbc.properties"/>
      <!-- 配置C3P0連接池 -->
      <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
          <!-- 屬性注入 =============== -->
          <property name="driverClass" value="${jdbc.driverClass}"></property>
          <property name="jdbcUrl" value="${jdbc.url}"></property>
          <property name="user" value="${jdbc.username}"></property>
          <property name="password" value="${jdbc.password}"></property>
      </bean> 
    
  • 測試能插入數(shù)據(jù)

2.4 使用JDBC模板進行CURD操作

2.4.1 增

package com.itzhouq.spring.jdbc.demo1;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value="classpath:applicationContext.xml")
public class JdbcDemo2 {
    @Resource(name="jdbcTemplate")
    private JdbcTemplate jdbcTemplate;
    
    @Test
    public void test1() {
        jdbcTemplate.update("insert into account values (null, ?,?)", "YYY",10000d);
    }
}

2.4.2 刪

@Test
    public void test3() {//刪除
        jdbcTemplate.update("delete from account where id = ?", 5);
    }

2.4.3 改

@Test
    public void test2() {//修改
        jdbcTemplate.update("update account set name = ?, money =  ? where id = ?", "何巨濤",10000d, 6);
    }

2.4.4 查

  • 查詢某個屬性

    @Test
      public void test4() {//查詢某個屬性
          String name = jdbcTemplate.queryForObject("select name from account where id = ?", String.class, 6);
          System.out.println(name);
      }
    
  • 查詢個數(shù)

    @Test
      public void test5() {//查詢個數(shù)
          Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
          System.out.println(count);
      }
    
  • 返回的是對象

    • 首先要創(chuàng)建一個實體Account

      @Test
          public void test6() {//封裝到一個對象中
              Account  account = jdbcTemplate.queryForObject("select * from account where id = ?", new MyRowMapper(), 4);
              System.out.println(account);
          }
          
          class MyRowMapper  implements RowMapper<Account>{
      
              @Override
              public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
                  Account account = new Account();
                  account.setId(rs.getInt("id"));
                  account.setName(rs.getString("name"));
                  account.setMoney(rs.getDouble("money"));
                  return account;
              }
          }
      
    • queryForObject的第二個參數(shù)需要實現(xiàn)一個接口RowMapper

  • 查詢多條記錄

    @Test
      public void test7() {
          List<Account> listAccount = jdbcTemplate.query("select * from account", new MyRowMapper());
          for (Account account : listAccount) {
              System.out.println(account);
          }
      }
      
      class MyRowMapper  implements RowMapper<Account>{
    
          @Override
          public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
              Account account = new Account();
              account.setId(rs.getInt("id"));
              account.setName(rs.getString("name"));
              account.setMoney(rs.getDouble("money"));
              return account;
          }
      }
    

3. Spring的事務(wù)管理

3.1 事務(wù)的回顧

3.1.1 什么是事務(wù)

  • 事務(wù):邏輯上的一組操作厘灼,組成這組操作的各個單元灾票,要么全都成功,要么全都失敗术幔。

3.1.2 事務(wù)的特性

  • 原子性:事務(wù)不可分割
  • 一致性:事務(wù)執(zhí)行前后數(shù)據(jù)完整性保持一致
  • 隔離性:一個事務(wù)的執(zhí)行不應(yīng)該受到其他事務(wù)的影響
  • 持久性:一旦事務(wù)結(jié)束元咙,數(shù)據(jù)就持久化到數(shù)據(jù)庫中

3.1.3 不考慮事務(wù)的隔離性引發(fā)安全問題

  • 讀問題
    • 臟讀:一個事務(wù)讀到另一個事務(wù)未提交的數(shù)據(jù)
    • 不可重復(fù)讀:一個事務(wù)讀取到另一個事務(wù)已經(jīng)提交的update的數(shù)據(jù)梯影,導(dǎo)致一個事務(wù)中多次查詢結(jié)果不一致
    • 虛讀、幻讀:一個事務(wù)讀到另一個事務(wù)已經(jīng)提交的insert數(shù)據(jù)庶香,導(dǎo)致一個事務(wù)中多次查詢結(jié)果不一致
  • 寫問題
    • 丟失更新

3.1.4 解決讀問題

  • 設(shè)置事務(wù)的隔離級別
    • read uncommited:未提交讀甲棍,任何讀問題都解決不了
    • Read commited:已提交讀,解決臟讀脉课,但是不可重復(fù)讀和虛讀有可能發(fā)生
    • Repeatable read:重復(fù)讀救军,解決臟讀和不可重復(fù)讀财异,但是虛讀有可能發(fā)生
    • Serializable:解決所有讀問題

3.2 Spring的事務(wù)管理的API

3.2. 1 PlatformTransactionManager:平臺事務(wù)管理器

  • 平臺事務(wù)管理器:接口,是Spring用于管理事務(wù)的真正的對象唱遭。
    • DataSourceTransactionManager:底層使用JDBC管理事務(wù)
    • HibernateTransactionManager:底層使用Hibernate管理事務(wù)

3.2.2 TransactionDefinition:事務(wù)定義信息

  • 事務(wù)定義:用于定義事務(wù)的相關(guān)的信息戳寸,隔離級別、超時信息拷泽、傳播行為疫鹊、是否只讀

3.2.3 TransactionStatus:事務(wù)的狀態(tài)

  • l 事務(wù)狀態(tài):用于記錄在事務(wù)管理過程中,事務(wù)的狀態(tài)的對象司致。

3.2.4 事務(wù)管理的API的關(guān)系

  • Spring進行事務(wù)管理的時候拆吆,首先平臺事務(wù)管理器根據(jù)事務(wù)定義信息進行事務(wù)的管理,在事務(wù)管理過程中脂矫,產(chǎn)生各種狀態(tài)枣耀,將這些狀態(tài)的信息記錄到事務(wù)狀態(tài)的對象中。

3.3 事務(wù)的傳播行為

  • Spring中提供了七種事務(wù)的傳播行為

3.3.1 保證多個操作在同一個事務(wù)中

  • PROPAGATION_REQUIRED:默認(rèn)值庭再,如果A中有事務(wù)捞奕,使用A中的事務(wù),如果A沒有拄轻,創(chuàng)建一個新的事務(wù)颅围,將操作包含進來
  • PROPAGATION_SUPPORTS:支持事務(wù),如果A中有事務(wù)恨搓,使用A中的事務(wù)院促。如果A沒有事務(wù),不使用事務(wù)斧抱。
  • PROPAGATION_MANDATORY:如果A中有事務(wù)常拓,使用A中的事務(wù)。如果A沒有事務(wù)夺姑,拋出異常墩邀。

3.3.2 保證多個操作不在同一個事務(wù)中

  • PROPAGATION_REQUIRES_NEW:如果A中有事務(wù),將A的事務(wù)掛起(暫停)盏浙,創(chuàng)建新事務(wù)眉睹,只包含自身操作。如果A中沒有事務(wù)废膘,創(chuàng)建一個新事務(wù)竹海,包含自身操作。
  • PROPAGATION_NOT_SUPPORTED:如果A中有事務(wù)丐黄,將A的事務(wù)掛起斋配。不使用事務(wù)管理。
  • PROPAGATION_NEVER:如果A中有事務(wù),報異常艰争。

3.3.3 嵌套式事務(wù)

  • PROPAGATION_NESTED:嵌套事務(wù)坏瞄,如果A中有事務(wù),按照A的事務(wù)執(zhí)行甩卓,執(zhí)行完成后鸠匀,設(shè)置一個保存點,執(zhí)行B中的操作逾柿,如果沒有異常缀棍,執(zhí)行通過,如果有異常机错,可以選擇回滾到最初始位置爬范,也可以回滾到保存點。

3.4 事務(wù)的管理

3.4.1 案例:轉(zhuǎn)賬

  • 創(chuàng)建AccountService接口

    package com.itzhouq.spring.tx.demo1;
    /*
     * 轉(zhuǎn)賬的業(yè)務(wù)層的接口
     */
    public interface AccountService {
      public void transfer(String from, String to, Double money);
    }
    
  • 創(chuàng)建接口實現(xiàn)類AccountServiceImpl

    package com.itzhouq.spring.tx.demo1;
    /*
     * 轉(zhuǎn)賬的業(yè)務(wù)層的實現(xiàn)類
     */
    public class AccountServiceImpl implements AccountService {
      
      //注入DAO
      private AccountDao accountDao;
      public void setAccountDao(AccountDao accountDao) {
          this.accountDao = accountDao;
      }
    
      /**
       *  from:轉(zhuǎn)出賬戶
       *  to:轉(zhuǎn)入賬戶
       *  money:轉(zhuǎn)賬金額
       */
      @Override
      public void transfer(String from, String to, Double money) {
          accountDao.outMoney(from, money);
          int i = 1 / 0;
          accountDao.inMoney(to, money);
      }
    }
    
  • 創(chuàng)建AccountDao接口

    package com.itzhouq.spring.tx.demo1;
    /*
     * 轉(zhuǎn)賬的DAO的接口
     */
    public interface AccountDao {
      public void outMoney(String from, Double money);
      public void inMoney(String to, Double money);
    }
    
  • 創(chuàng)建AccountDao實現(xiàn)類AccountDaoImpl

    package com.itzhouq.spring.tx.demo1;
    
    import org.springframework.jdbc.core.support.JdbcDaoSupport;
    
    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
      
      @Override
      public void outMoney(String from, Double money) {
          this.getJdbcTemplate().update("update account set money=money-? where name=?", money,from);
      }
    
      @Override
      public void inMoney(String to, Double money) {
          this.getJdbcTemplate().update("update account set money=money+? where name=?", money,to);
      }
    }
    
  • 配置Service和Dao:交給Spring管理

    • 復(fù)制applicationContext.xml文件弱匪,新建配置文件tx.xml

      <!-- 配置Service -->
          <bean id="accountService" class="com.itzhouq.spring.tx.demo1.AccountServiceImpl">
              <property name="accountDao" ref="accountDao"></property>
          </bean>
          
          <!-- 配置Dao -->
          <bean id="accountDao" class="com.itzhouq.spring.tx.demo1.AccountDaoImpl">
              
          </bean>
      
  • 在Dao中編寫扣錢和加錢

    • 配置連接池和JDBC的模板

      <!-- 引入連接數(shù)據(jù)的屬性文件 -->
      <context:property-placeholder location="classpath:jdbc.properties"/>
      <!-- 配置C3P0連接池 -->
      <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
          <!-- 屬性注入 =============== -->
          <property name="driverClass" value="${jdbc.driverClass}"></property>
          <property name="jdbcUrl" value="${jdbc.url}"></property>
          <property name="user" value="${jdbc.username}"></property>
          <property name="password" value="${jdbc.password}"></property>
      </bean> 
      <!-- 配置Spring的JDBC模板 =================-->
      <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
          <property name="dataSource" ref="dataSource"></property>
      </bean>
      
    • 在Dao中注入jdbc的模板

      • 方法一:在Dao中直接注入

        package com.itzhouq.spring.tx.demo1;
        
        import org.springframework.jdbc.core.JdbcTemplate;
        
        public class AccountDaoImpl implements AccountDao {
          
          private JdbcTemplate jdbcTemplate;
          public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
              this.jdbcTemplate = jdbcTemplate;
          }
        
          @Override
          public void outMoney(String from, Double money) {
              
        
          }
        
          @Override
          public void inMoney(String to, Double money) {
        
          }
        }
        
      • 方法二:AccountDaoImpl繼承JdbcDaoSupport

        • JdbcDaoSupport類中提供了模板青瀑,也提供了set方法

          package com.itzhouq.spring.tx.demo1;
          
          import org.springframework.jdbc.core.support.JdbcDaoSupport;
          
          public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
              
              @Override
              public void outMoney(String from, Double money) {
          
              }
          
              @Override
              public void inMoney(String to, Double money) {
          
              }
          }
          
        • 所以直接在xml文件中的dao注入模板

          <!-- 配置Dao -->
              <bean id="accountDao" class="com.itzhouq.spring.tx.demo1.AccountDaoImpl">
                  <property name="jdbcTemplate" ref="jdbcTemplate"></property>
              </bean>
          
      • 方法三:繼承JdbcDaoSupport類之后直接注入連接池,這樣連接池可以自動創(chuàng)建模板

        • 不用配置模板痢法,直接dao中注入連接池

          <!--可以省略的配置-->
          <!-- 配置Spring的JDBC模板 =================-->
              <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
                  <property name="dataSource" ref="dataSource"></property>
              </bean>
          
          <!-- 配置Dao -->
              <bean id="accountDao" class="com.itzhouq.spring.tx.demo1.AccountDaoImpl">
                  <property name="dataSource" ref="dataSource"/>
              </bean>
          
  • 配置文件

    <?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: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">
        
        <!-- 配置Service -->
        <bean id="accountService" class="com.itzhouq.spring.tx.demo1.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"></property>
        </bean>
        
        <!-- 配置Dao -->
        <bean id="accountDao" class="com.itzhouq.spring.tx.demo1.AccountDaoImpl">
            <property name="dataSource" ref="dataSource"/>
        </bean> 
        
        <!-- 引入連接數(shù)據(jù)的屬性文件 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
        <!-- 配置C3P0連接池 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <!-- 屬性注入 =============== -->
            <property name="driverClass" value="${jdbc.driverClass}"></property>
            <property name="jdbcUrl" value="${jdbc.url}"></property>
            <property name="user" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean> 
        <!-- 配置Spring的JDBC模板 =================-->
        <!-- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean> -->
            
    </beans>
    
    
  • 測試

    package com.itzhouq.spring.tx.demo1;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /*
     * 測試轉(zhuǎn)賬
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:tx.xml")
    public class SpringDemo1 {
      @Resource(name="accountService")
      private AccountService accountService;
      
      @Test
      public void test1() {
          accountService.transfer("周杰倫", "鄧超", 1000d);
      }
    }
    
    • 如果沒有異常能夠轉(zhuǎn)賬成功狱窘。但是沒有事務(wù)控制杜顺,一旦轉(zhuǎn)賬過程中出現(xiàn)異常就會出現(xiàn)數(shù)據(jù)丟失的現(xiàn)象财搁。

3.4.2 Spring的事務(wù)管理

  • 方式一:編程式事務(wù),需要手動編碼【了解】
  • 方式二:聲明式事務(wù)躬络,通過配置實現(xiàn)---AOP

3.4.3 聲明式事務(wù)

  • XML方式的聲明式的事務(wù)管理

    • 引入jar包

      • aop聯(lián)盟包

      • aspectJ包

      • aop包

      • Spring整合aspectJ包

        image
    • 配置事務(wù)管理器

      <!-- 配置事務(wù)管理器 -->
          <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource" ref="dataSource"/>
          </bean>
      
    • 配置增強

      <!-- 配置事務(wù)的增強=============================== -->
          <tx:advice id="txAdvice" transaction-manager="transactionManager">
              <tx:attributes>
                  <!-- 事務(wù)管理的規(guī)則 -->
                  <!-- <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"/>
                  <tx:method name="update*" propagation="REQUIRED"/>
                  <tx:method name="delete*" propagation="REQUIRED"/>
                  <tx:method name="find*" read-only="true"/> -->
                  <tx:method name="*" propagation="REQUIRED" read-only="false"/>
              </tx:attributes>
          </tx:advice>
      
    • AOP的配置

      <!-- aop的配置 -->
          <aop:config>
              <aop:pointcut expression="execution(* com.itzhouq.spring.tx.demo1.AccountServiceImpl.*(..))" id="pointcut1"/>
              <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
          </aop:config>
      
      • 測試:如果轉(zhuǎn)賬出現(xiàn)異常尖奔,事務(wù)自動回滾
  • 注解方式聲明事務(wù)

    • 引入aop的開發(fā)包

    • 配置事務(wù)管理

      <!-- 配置事務(wù)管理器=============================== -->
          <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource" ref="dataSource"/>
          </bean>
      
    • 開啟注解事務(wù)

      <!-- 開啟注解事務(wù)================================ -->
          <tx:annotation-driven transaction-manager="transactionManager"/>
      
    • 在業(yè)務(wù)層添加注解

      @Transactional
      public class AccountServiceImpl implements AccountService {
          
      
    • 測試:如果轉(zhuǎn)賬過程中出現(xiàn)異常,則自動回滾穷当。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末提茁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子馁菜,更是在濱河造成了極大的恐慌茴扁,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汪疮,死亡現(xiàn)場離奇詭異峭火,居然都是意外死亡,警方通過查閱死者的電腦和手機智嚷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門卖丸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盏道,你說我怎么就攤上這事稍浆。” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵衅枫,是天一觀的道長嫁艇。 經(jīng)常有香客問我,道長弦撩,這世上最難降的妖魔是什么裳仆? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮孤钦,結(jié)果婚禮上歧斟,老公的妹妹穿的比我還像新娘。我一直安慰自己偏形,他們只是感情好静袖,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著俊扭,像睡著了一般队橙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上萨惑,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天捐康,我揣著相機與錄音,去河邊找鬼庸蔼。 笑死解总,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的姐仅。 我是一名探鬼主播花枫,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼掏膏!你這毒婦竟也來了劳翰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤馒疹,失蹤者是張志新(化名)和其女友劉穎佳簸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颖变,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡生均,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了悼做。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疯特。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肛走,靈堂內(nèi)的尸體忽然破棺而出漓雅,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布邻吞,位于F島的核電站组题,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏抱冷。R本人自食惡果不足惜崔列,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望旺遮。 院中可真熱鬧赵讯,春花似錦、人聲如沸耿眉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸣剪。三九已至组底,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筐骇,已是汗流浹背债鸡。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留铛纬,地道東北人厌均。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像饺鹃,于是被迫代替她去往敵國和親莫秆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容