Spring筆記04_AOP註解開發_模板_事務

1. Spring基於AspectJ的註解的AOP開發

1. 1 SpringAOP的註解入門

  • 建立項目,導入jar包java

    • 須要導入Spring基礎包4+2
    • 須要導入AOP聯盟包、AspectJ包、Spring整合Aspect包Spring-aop包
    • Spring整合單元測試包

  • 引入配置文件applicationContext.xmlmysql

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

    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 "周杰倫";
        }
    }
  • 配置目標類,將目標類OrderDao交給Spring管理sql

    • 在applicationContext.xml中添加
    <!-- 配置目標類 -->
        <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對目標類的方法進行加強數據庫

    • 首先在配置文件中打開註解的AOP開發express

      <!-- 在配置文件總開啓註解的AOP開發 -->
          <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    • 在切面類上使用註解
/*
     * 切面類:註解的切面類
     */
    @Aspect    //標記該類爲切面類
    public class MyAspectAnno {
        
        @Before(value="execution(* com.itzhouq.spring.demo1.OrderDao.save(..))")
        public void before() {//這個註解用來標記目標類的哪一個方法使用何種加強
            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註解開發測試
   */
  
  @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方法上使用後置通知
  • 在切面類中添加如下方法apache

    //後置通知
        @AfterReturning("execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))")
        public void afterReturning() {
            System.out.println("後置加強==================");
        }
  • 不用修改目標類直接測試就能實現效果編程

    前置加強===============
    保存訂單。。。
    修改訂單。。。
    查找訂單。。。
    刪除訂單。。。
    後置加強==================
  • 後置通知還能夠使用返回值安全

    //後置通知
        @AfterReturning(value="execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))", returning="result")
        public void afterReturning(Object result) {
            System.out.println("後置加強=================="+result);
        }
    前置加強===============
    保存訂單。。。
    修改訂單。。。
    查找訂單。。。
    刪除訂單。。。
    後置加強==================周杰倫

1.2.3 環繞通知

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

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

    前置加強===============
    保存訂單。。。
    環繞前加強===========
    修改訂單。。。
    環繞前加強===========
    查找訂單。。。
    刪除訂單。。。
    後置加強==================周杰倫

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;
        }
  • 測試

    前置加強===============
    保存訂單。。。
    環繞前加強===========
    修改訂單。。。
    環繞前加強===========
    查找訂單。。。
    異常拋出通知============java.lang.ArithmeticException: / by zero

1.2.5 最終通知‘

  • 在切面類中添加方法

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

    前置加強===============
    保存訂單。。。
    環繞前加強===========
    修改訂單。。。
    環繞前加強===========
    查找訂單。。。
    最終加強============
    異常拋出通知============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    //標記該類爲切面類
  public class MyAspectAnno {
      
      @Before(value="MyAspectAnno.pointcut2()")
      public void before() {//這個註解用來標記目標類的哪一個方法使用何種加強
          System.out.println("前置加強===============");
      }
      
      //後置通知
      @AfterReturning(value="execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))", returning="result")
      public void afterReturning(Object result) {
          System.out.println("後置加強=================="+result);
      }
      
      //環繞通知
      @Around(value="MyAspectAnno.pointcut3()")
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
          System.out.println("環繞前加強===========");
          Object obj = joinPoint.proceed();
          System.out.println("環繞前加強===========");
          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開發的一站式的框架,有EE開發中每一層的解決方案。Spring對持久層也提供瞭解決方案:ORM模塊和JDBC的模塊。
  • Spring提供了不少的模板用於簡化開發。

2.1.1 JDBC模板使用的入門

  • 建立項目,引入jar包

    • 引入基本的4+2包
    • 數據庫驅動包
    • Spring的JDBC模板的jar包:事務管理tx和jdbc的包
    • 單元測試包

  • 建立數據庫和表

    create database spring4_day03;
    use spring4_day03;
    create table account(
        id int primary key auto_increment,
        name varchar(20),
        money double
    )
  • 使用JDBC模板:保存數據
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() {
          //建立鏈接池    這裏使用Spring默認的鏈接池
          DriverManagerDataSource dataSource = new DriverManagerDataSource();
          dataSource.setDriverClassName("com.mysql.jdbc.Driver");
          dataSource.setUrl("jdbc:mysql:///spring4_day03");
          dataSource.setUsername("root");
          dataSource.setPassword("2626");
          
          //建立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
  • 測試能插入數據

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的內置鏈接池,將鏈接池交給Spring管理

    <!-- 配置Spring的內置鏈接池 -->
        <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);
        }
    }
  • 測試能插入數據

2.2 使用開源的數據庫鏈接池

2.2.1 C3P0的使用

  • 導入jar包

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

    • ..spring-framework-3.0.2.RELEASE-dependenciescom.mchange.c3p0com.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
    • 測試能插入數據

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-dependenciesorg.apache.commonscom.springsource.org.apache.commons.dbcp
    • ..spring-framework-3.0.2.RELEASE-dependenciesorg.apache.commonscom.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
  • 測試能插入數據

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的配置文件中引入屬性文件

    <!-- 引入鏈接數據的屬性文件 -->
        <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>
  • 測試能插入數據

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);
        }
  • 查詢個數

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

    • 首先要建立一個實體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的第二個參數須要實現一個接口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的事務管理

3.1 事務的回顧

3.1.1 什麼是事務

  • 事務:邏輯上的一組操做,組成這組操做的各個單元,要麼全都成功,要麼全都失敗。

3.1.2 事務的特性

  • 原子性:事務不可分割
  • 一致性:事務執行先後數據完整性保持一致
  • 隔離性:一個事務的執行不該該受到其餘事務的影響
  • 持久性:一旦事務結束,數據就持久化到數據庫中

3.1.3 不考慮事務的隔離性引起安全問題

  • 讀問題

    • 髒讀:一個事務讀到另外一個事務未提交的數據
    • 不可重複讀:一個事務讀取到另外一個事務已經提交的update的數據,致使一個事務中屢次查詢結果不一致
    • 虛讀、幻讀:一個事務讀到另外一個事務已經提交的insert數據,致使一個事務中屢次查詢結果不一致
  • 寫問題

    • 丟失更新

3.1.4 解決讀問題

  • 設置事務的隔離級別

    • read uncommited:未提交讀,任何讀問題都解決不了
    • Read commited:已提交讀,解決髒讀,可是不可重複讀和虛讀有可能發生
    • Repeatable read:重複讀,解決髒讀和不可重複讀,可是虛讀有可能發生
    • Serializable:解決全部讀問題

3.2 Spring的事務管理的API

3.2. 1 PlatformTransactionManager:平臺事務管理器

  • 平臺事務管理器:接口,是Spring用於管理事務的真正的對象。

    • DataSourceTransactionManager:底層使用JDBC管理事務
    • HibernateTransactionManager:底層使用Hibernate管理事務

3.2.2 TransactionDefinition:事務定義信息

  • 事務定義:用於定義事務的相關的信息,隔離級別、超時信息、傳播行爲、是否只讀

3.2.3 TransactionStatus:事務的狀態

  • l 事務狀態:用於記錄在事務管理過程當中,事務的狀態的對象。

3.2.4 事務管理的API的關係

  • Spring進行事務管理的時候,首先平臺事務管理器根據事務定義信息進行事務的管理,在事務管理過程當中,產生各類狀態,將這些狀態的信息記錄到事務狀態的對象中。

3.3 事務的傳播行爲

  • Spring中提供了七種事務的傳播行爲

3.3.1 保證多個操做在同一個事務中

  • PROPAGATION_REQUIRED:默認值,若是A中有事務,使用A中的事務,若是A沒有,建立一個新的事務,將操做包含進來
  • PROPAGATION_SUPPORTS:支持事務,若是A中有事務,使用A中的事務。若是A沒有事務,不使用事務。
  • PROPAGATION_MANDATORY:若是A中有事務,使用A中的事務。若是A沒有事務,拋出異常。

3.3.2 保證多個操做不在同一個事務中

  • PROPAGATION_REQUIRES_NEW:若是A中有事務,將A的事務掛起(暫停),建立新事務,只包含自身操做。若是A中沒有事務,建立一個新事務,包含自身操做。
  • PROPAGATION_NOT_SUPPORTED:若是A中有事務,將A的事務掛起。不使用事務管理。
  • PROPAGATION_NEVER:若是A中有事務,報異常。

3.3.3 嵌套式事務

  • PROPAGATION_NESTED:嵌套事務,若是A中有事務,按照A的事務執行,執行完成後,設置一個保存點,執行B中的操做,若是沒有異常,執行經過,若是有異常,能夠選擇回滾到最初始位置,也能夠回滾到保存點。

3.4 事務的管理

3.4.1 案例:轉帳

  • 建立AccountService接口
package com.itzhouq.spring.tx.demo1;
  /*
   * 轉帳的業務層的接口
   */
  public interface AccountService {
      public void transfer(String from, String to, Double money);
  }
  • 建立接口實現類AccountServiceImpl
package com.itzhouq.spring.tx.demo1;
  /*
   * 轉帳的業務層的實現類
   */
  public class AccountServiceImpl implements AccountService {
      
      //注入DAO
      private AccountDao accountDao;
      public void setAccountDao(AccountDao accountDao) {
          this.accountDao = accountDao;
      }
  
      /**
       *     from:轉出帳戶
       *     to:轉入帳戶
       *     money:轉帳金額
       */
      @Override
      public void transfer(String from, String to, Double money) {
          accountDao.outMoney(from, money);
          int i = 1 / 0;
          accountDao.inMoney(to, money);
      }
  }
  • 建立AccountDao接口
package com.itzhouq.spring.tx.demo1;
  /*
   * 轉帳的DAO的接口
   */
  public interface AccountDao {
      public void outMoney(String from, Double money);
      public void inMoney(String to, Double money);
  }
  • 建立AccountDao實現類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管理

    • 複製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的模板

      <!-- 引入鏈接數據的屬性文件 -->
      <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類以後直接注入鏈接池,這樣鏈接池能夠自動建立模板

        • 不用配置模板,直接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> 
          
          <!-- 引入鏈接數據的屬性文件 -->
          <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;
  
  /*
   * 測試轉帳
   */
  @RunWith(SpringJUnit4ClassRunner.class)
  @ContextConfiguration("classpath:tx.xml")
  public class SpringDemo1 {
      @Resource(name="accountService")
      private AccountService accountService;
      
      @Test
      public void test1() {
          accountService.transfer("周杰倫", "鄧超", 1000d);
      }
  }
  • 若是沒有異常可以轉帳成功。可是沒有事務控制,一旦轉帳過程當中出現異常就會出現數據丟失的現象。

3.4.2 Spring的事務管理

  • 方式一:編程式事務,須要手動編碼【瞭解】
  • 方式二:聲明式事務,經過配置實現---AOP

3.4.3 聲明式事務

  • XML方式的聲明式的事務管理

    • 引入jar包

      • aop聯盟包
      • aspectJ包
      • aop包
      • Spring整合aspectJ包

    • 配置事務管理器

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

      <!-- 配置事務的加強=============================== -->
          <tx:advice id="txAdvice" transaction-manager="transactionManager">
              <tx:attributes>
                  <!-- 事務管理的規則 -->
                  <!-- <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>
      • 測試:若是轉帳出現異常,事務自動回滾
  • 註解方式聲明事務

    • 引入aop的開發包
    • 配置事務管理

      <!-- 配置事務管理器=============================== -->
          <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource" ref="dataSource"/>
          </bean>
    • 開啓註解事務

      <!-- 開啓註解事務================================ -->
          <tx:annotation-driven transaction-manager="transactionManager"/>
    • 在業務層添加註解

      @Transactional
      public class AccountServiceImpl implements AccountService {
    • 測試:若是轉帳過程當中出現異常,則自動回滾。
相關文章
相關標籤/搜索