Spring學習筆記3(JDBC模板&事務管理)

Jaybo發表於2018-08-22

前言

Spring框架的學習路線:

  1. Spring第一天:Spring的IOC容器之XML的方式,Spring框架與Web專案整合
  2. Spring第二天:Spring的IOC容器之註解的方式,Spring的AOP技術
  3. Spring第三天:Spring的事務管理、Spring框架的JDBC模板
  4. Spring第四天:SSH三大框架的整合

這是第三天學習大綱:

Spring學習筆記3(JDBC模板&事務管理)

一、Spring框架的AOP之註解的方式

大概步驟如下:

1. 步驟一:建立JavaWEB專案,引入具體的開發的jar包
    * 先引入Spring框架開發的基本開發包
    * 再引入Spring框架的AOP的開發包
        * spring的傳統AOP的開發的包
            * spring-aop-4.2.4.RELEASE.jar
            * com.springsource.org.aopalliance-1.0.0.jar

        * aspectJ的開發包
            * com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
            * spring-aspects-4.2.4.RELEASE.jar

2. 步驟二:建立Spring的配置檔案,引入具體的AOP的schema約束
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    </beans>

3. 步驟三:建立包結構,編寫具體的介面和實現類
    * com.itheima.demo1
        * CustomerDao           -- 介面
        * CustomerDaoImpl       -- 實現類

4. 步驟四:將目標類配置到Spring中
    <bean id="customerDao" class="com.itheima.demo1.CustomerDaoImpl"/>

5. 步驟五:定義切面類
    * 新增切面和通知的註解
        * @Aspect                   -- 定義切面類的註解

        * 通知型別(註解的引數是切入點的表示式)
            * @Before               -- 前置通知
            * @AfterReturing        -- 後置通知
            * @Around               -- 環繞通知
            * @After                -- 最終通知
            * @AfterThrowing        -- 異常丟擲通知

    * 具體的程式碼如下
        @Aspect
        public class MyAspectAnno {
            @Before(value="execution(public void com.itheima.demo1.CustomerDaoImpl.save())")
            public void log(){
                System.out.println("記錄日誌...");
            }
        }

6. 步驟六:在配置檔案中定義切面類
    <bean id="myAspectAnno" class="com.itheima.demo1.MyAspectAnno"/>

7. 步驟七:在配置檔案中開啟自動代理
    <aop:aspectj-autoproxy/>

8. 完成測試
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class Demo1 {

        @Resource(name="customerDao")
        private CustomerDao customerDao;

        @Test
        public void run1(){
            customerDao.save();
            customerDao.update();
        }
    }
複製程式碼

-----------------------------------------------直接上程式碼學習-------------------------------------------------

MyAspectAnno.java:

/**
 * 註解方式的切面類
 * @author Administrator
 */
@Aspect
public class MyAspectAnno {
	/** 方式1:
    @Before(value="execution(public * com.itheima.demo1.CustomerDaoImpl.save())")
	public void log(){
		System.out.println("記錄日誌...");
	}
    */
	/** 方式2:(使用自定義切入點)
	 * 通知型別:@Before前置通知(切入點的表示式)
	 */
	@Before(value="MyAspectAnno.fn()")
	public void log(){
		System.out.println("記錄日誌...");
	}
	
	/**
	 * 最終通知:方法執行成功或者右異常,都會執行
	 */
	@After(value="MyAspectAnno.fn()")
	public void after(){
		System.out.println("最終通知...");
	}
	
	/**
	 * 環繞通知
	 */
	@Around(value="MyAspectAnno.fn()")
	public void around(ProceedingJoinPoint joinPoint){
		System.out.println("環繞通知1...");
		try {
			// 讓目標物件的方法執行
			joinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("環繞通知2...");
	}
	
	/**
	 * 自動定義切入點	@Pointcut
	 */
	@Pointcut(value="execution(public * com.itheima.demo1.CustomerDaoImpl.save())")
	public void fn(){}
	
}
複製程式碼

applicationContext.xml 新增:

<!-- 開啟自動代理 -->
<aop:aspectj-autoproxy/>

<!-- 配置目標物件 -->
<bean id="customerDao" class="com.itheima.demo1.CustomerDaoImpl"/>

<!-- 配置切面類 -->
<bean id="myAspectAnno" class="com.itheima.demo1.MyAspectAnno"/>
複製程式碼

測試 Demo1.java:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo1 {
    @Resource(name="customerDao")
    private CustomerDao customerDao;

    @Test
    public void run1(){
        customerDao.save();
        customerDao.update();
    }
}
複製程式碼

通知型別:

1. 通知型別
    * @Before               -- 前置通知
    * @AfterReturing        -- 後置通知
    * @Around               -- 環繞通知(目標物件方法預設不執行的,需要手動執行)
    * @After                -- 最終通知
    * @AfterThrowing        -- 異常丟擲通知

2. 配置通用的切入點
    * 使用@Pointcut定義通用的切入點

    @Aspect
    public class MyAspectAnno {
        @Before(value="MyAspectAnno.fn()")
        public void log(){
            System.out.println("記錄日誌...");
        }
        @Pointcut(value="execution(public void com.itheima.demo1.CustomerDaoImpl.save())")
        public void fn(){}
    }
複製程式碼

二、Spring框架的JDBC模板

2.1 JDBC的模板類的演示

1. 步驟一:建立資料庫的表結構
    create database spring_day03;
    use spring_day03;
    create table t_account(
        id int primary key auto_increment,
        name varchar(20),
        money double
    );

2. 引入開發的jar包
    * 先引入IOC基本的6個jar包
    * 再引入Spring-aop的jar包
    * 最後引入JDBC模板需要的jar包
        * MySQL資料庫的驅動包:mysql-connector-java-5.1.7-bin.jar
        * Spring-jdbc.jar
        * Spring-tx.jar

3. 編寫測試程式碼(自己來new物件的方式)
    @Test
    public void run1(){
        // 建立連線池,先使用Spring框架內建的連線池
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///spring_day03");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        // 建立模板類
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        // 完成資料的新增
        jdbcTemplate.update("insert into t_account values (null,?,?)", "測試",10000);
    }
複製程式碼

如上使用的為 Spring 框架內建的連線池

2.2 使用Spring框架來管理模板類

上節中直接在測試程式碼中編寫(即自己 new 物件的方式),我們難道不可以把連線池、模板類交給 Spring 管理嗎?可以的。如下:

在 applicationContext.xml 新增:

<!-- 內建的連線池:先配置連線池 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql:///spring_day03"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</bean>

<!-- 配置JDBC的模板類 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>
複製程式碼

然後測試就可以這樣編寫了:

/**
 * 測試JDBC的模板類,使用IOC的方式
 * @author Administrator
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo1_1 {
	@Resource(name="jdbcTemplate")
	private JdbcTemplate jdbcTemplate;
	
	@Test
	public void run1(){
		jdbcTemplate.update("insert into t_account values (null,?,?)", "小蒼",10000);
	}
}
複製程式碼

總結步驟如下:

1. 剛才編寫的程式碼使用的是new的方式,應該把這些類交給Spring框架來管理。
2. 修改的步驟如下
    * 步驟一:Spring管理內建的連線池
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql:///spring_day03"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>

    * 步驟二:Spring管理模板類
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"/>
        </bean>

    * 步驟三:編寫測試程式
        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration("classpath:applicationContext.xml")
        public class Demo2 {

            @Resource(name="jdbcTemplate")
            private JdbcTemplate jdbcTemplate;

            @Test
            public void run2(){
                jdbcTemplate.update("insert into t_account values (null,?,?)", "測試2",10000);
            }
        }
複製程式碼

2.3 Spring框架管理開源的連線池

如果使用的不是 Spring 內建的連線池,使用的開源的連線池,該如何管理開源的連線池呢:

1. 管理DBCP連線池
    * 先引入DBCP的2個jar包
        * com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar
        * com.springsource.org.apache.commons.pool-1.5.3.jar

    * 編寫配置檔案
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql:///spring_day03"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>

2. 管理C3P0連線池
    * 先引入C3P0的jar包
        * com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar

    * 編寫配置檔案
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.jdbc.Driver"/>
            <property name="jdbcUrl" value="jdbc:mysql:///spring_day03"/>
            <property name="user" value="root"/>
            <property name="password" value="root"/>
        </bean>
複製程式碼

2.4 Spring框架的JDBC模板的簡單操作

1. 增刪改查的操作
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class SpringDemo3 {

        @Resource(name="jdbcTemplate")
        private JdbcTemplate jdbcTemplate;

        @Test
        // 插入操作
        public void demo1(){
            jdbcTemplate.update("insert into account values (null,?,?)", "冠希",10000d);
        }

        @Test
        // 修改操作
        public void demo2(){
            jdbcTemplate.update("update account set name=?,money =? where id = ?", "思雨",10000d,5);
        }

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

        @Test
        // 查詢一條記錄
        public void demo4(){
            Account account = jdbcTemplate.queryForObject("select * from account where id = ?", new BeanMapper(), 1);
            System.out.println(account);
        }

        @Test
        // 查詢所有記錄
        public void demo5(){
            List<Account> list = jdbcTemplate.query("select * from t_account", new BeanMapper());
            for (Account account : list) {
                System.out.println(account);
            }
        }
    }

    class BeanMapper implements RowMapper<Account>{
        public Account mapRow(ResultSet rs, int arg1) throws SQLException {
            Account account = new Account();
            account.setId(rs.getInt("id"));
            account.setName(rs.getString("name"));
            account.setMoney(rs.getDouble("money"));
            return account;
        }
    }
複製程式碼

查詢所有記錄 demo5( )方法的輸出結果如下:

Spring學習筆記3(JDBC模板&事務管理)

三、Spring框架的事務管理

3.1 事務的回顧

1. 事務:指的是邏輯上一組操作,組成這個事務的各個執行單元,要麼一起成功,要麼一起失敗!
2. 事務的特性
    * 原子性
    * 一致性
    * 隔離性
    * 永續性

3. 如果不考慮隔離性,引發安全性問題
    * 讀問題:
        * 髒讀:
        * 不可重複讀:
        * 虛讀:

    * 寫問題:
        * 丟失更新:

4. 如何解決安全性問題
    * 讀問題解決,設定資料庫隔離級別
    * 寫問題解決可以使用 悲觀鎖和樂觀鎖的方式解決
複製程式碼

3.2 Spring框架的事務管理相關的類和API

1. PlatformTransactionManager介面     -- 平臺事務管理器.(真正管理事務的類)。該介面有具體的實現類,根據不同的持久層框架,需要選擇不同的實現類!
2. TransactionDefinition介面          -- 事務定義資訊.(事務的隔離級別,傳播行為,超時,只讀)
3. TransactionStatus介面              -- 事務的狀態

4. 總結:上述物件之間的關係:平臺事務管理器真正管理事務物件.根據事務定義的資訊TransactionDefinition 進行事務管理,在管理事務中產生一些狀態.將狀態記錄到TransactionStatus中

5. PlatformTransactionManager介面中實現類和常用的方法
    1. 介面的實現類
        * 如果使用的Spring的JDBC模板或者MyBatis框架,需要選擇DataSourceTransactionManager實現類
        * 如果使用的是Hibernate的框架,需要選擇HibernateTransactionManager實現類

    2. 該介面的常用方法
        * void commit(TransactionStatus status) 
        * TransactionStatus getTransaction(TransactionDefinition definition) 
        * void rollback(TransactionStatus status) 

6. TransactionDefinition
    1. 事務隔離級別的常量
        * static int ISOLATION_DEFAULT                  -- 採用資料庫的預設隔離級別
        * static int ISOLATION_READ_UNCOMMITTED 
        * static int ISOLATION_READ_COMMITTED 
        * static int ISOLATION_REPEATABLE_READ 
        * static int ISOLATION_SERIALIZABLE 

    2. 事務的傳播行為常量(不用設定,使用預設值)
        * 先解釋什麼是事務的傳播行為:解決的是業務層之間的方法呼叫!!

        * PROPAGATION_REQUIRED(預設值) -- A中有事務,使用A中的事務.如果沒有,B就會開啟一個新的事務,將A包含進來.(保證A,B在同一個事務中),預設值!!
        * PROPAGATION_SUPPORTS          -- A中有事務,使用A中的事務.如果A中沒有事務.那麼B也不使用事務.
        * PROPAGATION_MANDATORY         -- A中有事務,使用A中的事務.如果A沒有事務.丟擲異常.

        * PROPAGATION_REQUIRES_NEW(記)-- A中有事務,將A中的事務掛起.B建立一個新的事務.(保證A,B沒有在一個事務中)
        * PROPAGATION_NOT_SUPPORTED     -- A中有事務,將A中的事務掛起.
        * PROPAGATION_NEVER             -- A中有事務,丟擲異常.

        * PROPAGATION_NESTED(記)     -- 巢狀事務.當A執行之後,就會在這個位置設定一個儲存點.如果B沒有問題.執行通過.如果B出現異常,執行客戶根據需求回滾(選擇回滾到儲存點或者是最初始狀態)
複製程式碼

關於事務的傳播行為的理解可以先看下圖:

Spring學習筆記3(JDBC模板&事務管理)

這裡 save 方法當做 A,find 方法當做 B,假設傳播行為設定為 PROPAGATION_SUPPORTS,則 A 中有事務,使用 A 中的事務,如果 A 中沒有事務,那麼 B 也不使用事務。

3.3 搭建事務管理轉賬案例的環境(強調:簡化開發,以後DAO可以繼承JdbcDaoSupport類)

1. 步驟一:建立WEB工程,引入需要的jar包
    * IOC的6個包
    * AOP的4個包
    * C3P0的1個包
    * MySQL的驅動包
    * JDBC目標2個包
    * 整合JUnit測試包

2. 步驟二:引入配置檔案
    * 引入配置檔案
        * 引入log4j.properties

        * 引入applicationContext.xml
            <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
                <property name="driverClass" value="com.mysql.jdbc.Driver"/>
                <property name="jdbcUrl" value="jdbc:mysql:///spring_day03"/>
                <property name="user" value="root"/>
                <property name="password" value="root"/>
            </bean>

3. 步驟三:建立對應的包結構和類
    * com.itheima.demo1
    * AccountService
    * AccountServlceImpl
    * AccountDao
    * AccountDaoImpl

4. 步驟四:引入Spring的配置檔案,將類配置到Spring中
    <bean id="accountService" class="com.itheima.demo1.AccountServiceImpl">
    </bean>

    <bean id="accountDao" class="com.itheima.demo1.AccountDaoImpl">
    </bean>

5. 步驟五:在業務層注入DAO ,在DAO中注入JDBC模板(強調:簡化開發,以後DAO可以繼承JdbcDaoSupport類)
    <bean id="accountService" class="com.itheima.demo1.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <bean id="accountDao" class="com.itheima.demo1.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

6. 步驟六:編寫DAO和Service中的方法
    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
        public void outMoney(String out, double money) {
            this.getJdbcTemplate().update("update t_account set money = money = ? where name = ?", money,out);
        }
        public void inMoney(String in, double money) {
            this.getJdbcTemplate().update("update t_account set money = money + ? where name = ?", money,in);
        }
    }

7. 步驟七:編寫測試程式.
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class Demo1 {

        @Resource(name="accountService")
        private AccountService accountService;

        @Test
        public void run1(){
            accountService.pay("冠希", "美美", 1000);
        }
    }
複製程式碼

關於步驟 5 中,AccountDaoImpl.java 程式碼按理應該如下這樣寫:

public class AccountDaoImpl implements AccountDao {
    private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    //扣錢
    public void outMoney(String out, double money) {
        jdbcTemplate.update("update t_account set money = money - ? where name = ?", money,out);
    }
    //加錢
    public void inMoney(String in, double money) {
        jdbcTemplate.update("update t_account set money = money + ? where name = ?", money,in);
    }
}
複製程式碼

按邏輯來說來說在業務層注入 DAO ,在 DAO 中注入 JDBC 模板,applicationContext.xml 新增如下:

<!-- 配置C3P0的連線池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql:///spring_day03"/>
    <property name="user" value="root"/>
    <property name="password" value="root"/>
</bean>
<!-- 配置JDBC的模板類。 在 JDBC 模板注入連線池-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- DAO 中注入 JDBC 模板 -->
<bean id="accountDao" class="com.itheima.demo1.AccountDaoImpl">
    <property name="jdbcTemplate" ref="jdbcTemplate"/> 
</bean>
複製程式碼

解釋: 畢竟業務層需要操作 Dao 層物件,Dao 層需要操作 JDBC 模板類物件,JDBC 需要連線池物件,所以如上。

但是為了簡化開發,其實 DAO 可以繼承 JdbcDaoSupport 這個類?AccountDaoImpl.java 可以如下編寫:(繼承 JdbcDaoSupport )

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    //扣錢
    public void outMoney(String out, double money) {
        this.getJdbcTemplate().update("update t_account set money = money - ? where name = ?", money,out);
    }
    //加錢
    public void inMoney(String in, double money) {
        this.getJdbcTemplate().update("update t_account set money = money + ? where name = ?", money,in);
    }
}
複製程式碼

JdbcDaoSupport 是一個什麼類?可以檢視原始碼發現:

Spring學習筆記3(JDBC模板&事務管理)

可以看到 setDataSource( ) 方法中如果模板類物件為空,會根據連線池建立一個模板,並賦值給成員屬性模板類物件 jdbcTemplate。這裡程式碼啥意思?劃重點,這裡程式碼表示建立模板物件之前的前提得是,我先有一個連線池,再根據連線池建立 JDBC 模板類物件。

所以在配置檔案寫注入配置的時候,DAO 層可以考慮這樣寫:

<bean id="accountDao" class="com.itheima.demo1.AccountDaoImpl">
    <!-- <property name="jdbcTemplate" ref="jdbcTemplate"/> -->
    <property name="dataSource" ref="dataSource"/>
</bean>
複製程式碼

直接注入連線池即可,而不需要再注入 JDBC 模板類。因為注入連線池之後,底層會根據連線池建立一個 JDBC 模板。(請再回頭看 JdbcDaoSupport 原始碼截圖體會。)

推導過程:

Spring學習筆記3(JDBC模板&事務管理)

3.4 Spring框架的事務管理的分類

1. Spring的事務管理的分類
    1. Spring的程式設計式事務管理(不推薦使用)
        * 通過手動編寫程式碼的方式完成事務的管理(不推薦)

    2. Spring的宣告式事務管理(底層採用AOP的技術)
        * 通過一段配置的方式完成事務的管理(重點掌握註解的方式)
複製程式碼

1、Spring框架的事務管理之程式設計式的事務管理(瞭解)

1. 說明:Spring為了簡化事務管理的程式碼:提供了模板類 TransactionTemplate,所以手動程式設計的方式來管理事務,只需要使用該模板類即可!!

2. 手動程式設計方式的具體步驟如下:
    1. 步驟一:配置一個事務管理器,Spring使用PlatformTransactionManager介面來管理事務,所以我們們需要使用到他的實現類!!
        <!-- 配置事務管理器 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>

    2. 步驟二:配置事務管理的模板
        <!-- 配置事務管理的模板 -->
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
            <property name="transactionManager" ref="transactionManager"/>
        </bean>

    3. 步驟三:在需要進行事務管理的類中,注入事務管理的模板.
        <bean id="accountService" class="com.itheima.demo1.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"/>
            <property name="transactionTemplate" ref="transactionTemplate"/>
        </bean>

    4. 步驟四:在業務層使用模板管理事務:
        // 注入事務模板物件
        private TransactionTemplate transactionTemplate;
        public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
            this.transactionTemplate = transactionTemplate;
        }

        public void pay(final String out, final String in, final double money) {
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {

                protected void doInTransactionWithoutResult(TransactionStatus status) {
                    // 扣錢
                    accountDao.outMoney(out, money);
                    int a = 10/0;
                    // 加錢
                    accountDao.inMoney(in, money);
                }
            });
        }
複製程式碼

推導過程思路:

Spring學習筆記3(JDBC模板&事務管理)

完整 applicationContext.xml 配置:

<!-- 配置C3P0的連線池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql:///spring_day03"/>
    <property name="user" value="root"/>
    <property name="password" value="root"/>
</bean>

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

<!-- 手動編碼,提供了模板類,使用該類管理事務比較簡單 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

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

<!-- 配置業務層和持久層 -->
<bean id="accountService" class="com.itheima.demo1.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"/>
    <property name="transactionTemplate" ref="transactionTemplate"/>
</bean>

<bean id="accountDao" class="com.itheima.demo1.AccountDaoImpl">
    <!-- <property name="jdbcTemplate" ref="jdbcTemplate"/> -->
    <property name="dataSource" ref="dataSource"/>
</bean>
複製程式碼

2、Spring框架的事務管理之宣告式事務管理,即通過配置檔案來完成事務管理(AOP思想)

宣告式事務管理又分成兩種方式:

  1. 基於AspectJ的XML方式(重點掌握)
  2. 基於AspectJ的註解方式(重點掌握)

① 基於AspectJ的XML方式:(重點掌握)

1. 步驟一:恢復轉賬開發環境

2. 步驟二:引入AOP的開發包

3. 步驟三:配置事務管理器
    <!-- 配置事務管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

4. 步驟四:配置事務增強
    <!-- 配置事務增強 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--
                name        :繫結事務的方法名,可以使用萬用字元,可以配置多個。
                propagation :傳播行為
                isolation   :隔離級別
                read-only   :是否只讀
                timeout     :超時資訊
                rollback-for:發生哪些異常回滾.
                no-rollback-for:發生哪些異常不回滾.
             -->
            <!-- 哪些方法加事務 -->
            <tx:method name="pay" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

5. 步驟五:配置AOP的切面
    <!-- 配置AOP切面產生代理 -->
    <aop:config>
        <aop:advisor advice-ref="myAdvice" pointcut="execution(* com.itheima.demo2.AccountServiceImpl.pay(..))"/>
    </aop:config>

    * 注意:如果是自己編寫的切面,使用<aop:aspect>標籤,如果是系統製作的,使用<aop:advisor>標籤。

6. 步驟六:編寫測試類
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext2.xml")
    public class Demo2 {

        @Resource(name="accountService")
        private AccountService accountService;

        @Test
        public void run1(){
            accountService.pay("冠希", "美美", 1000);
        }
    }
複製程式碼

applicationContext.xml:

<!-- 配置C3P0的連線池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql:///spring_day03"/>
    <property name="user" value="root"/>
    <property name="password" value="root"/>
</bean>

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

<!-- 宣告式事務(採用XML配置檔案的方式) -->
<!-- 先配置通知 -->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 給方法設定資料庫屬性(隔離級別,傳播行為) -->
        <tx:method name="pay" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<!-- 配置AOP:如果是自己編寫的AOP,使用aop:aspect配置,使用的是Spring框架提供的通知aop:advisor -->
<aop:config>
    <!-- aop:advisor,是Spring框架提供的通知 -->
    <aop:advisor advice-ref="myAdvice" pointcut="execution(public * com.itheima.demo2.AccountServiceImpl.pay(..))"/>
</aop:config>

<!-- 配置業務層和持久層 -->
<bean id="accountService" class="com.itheima.demo2.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"/>
</bean>

<bean id="accountDao" class="com.itheima.demo2.AccountDaoImpl">
    <property name="dataSource" ref="dataSource"/>
</bean>
複製程式碼

② 基於AspectJ的註解方式:(重點掌握,最簡單的方式)

1. 步驟一:恢復轉賬的開發環境

2. 步驟二:配置事務管理器
    <!-- 配置事務管理器  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

3. 步驟三:開啟註解事務
    <!-- 開啟註解事務 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

4. 步驟四:在業務層上新增一個註解:@Transactional

5. 編寫測試類
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext3.xml")
    public class Demo3 {

        @Resource(name="accountService")
        private AccountService accountService;

        @Test
        public void run1(){
            accountService.pay("冠希", "美美", 1000);
        }
    }
複製程式碼

注:關於步驟四,如果在類上新增了註解 @Transactional,則表示類中所有方法全部都有事務。

applicationContext.xml:

<!-- 配置C3P0的連線池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql:///spring_day03"/>
    <property name="user" value="root"/>
    <property name="password" value="root"/>
</bean>
 
<!-- 配置平臺事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 開啟事務的註解 -->
<tx:annotation-driven tr
                       ansaction-manager="transactionManager"/>

<!-- 配置業務層和持久層 -->
<bean id="accountService" class="com.itheima.demo3.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"/>
</bean>

<bean id="accountDao" class="com.itheima.demo3.AccountDaoImpl">
    <property name="dataSource" ref="dataSource"/>
</bean>
複製程式碼

相關文章