Spring系列之事務的控制 註解實現+xml實現+事務的隔離等級

一隻胡說八道的猴子發表於2020-10-02

Spring系列之事務的控制 註解實現+xml實現

在前面我寫過一篇關於事務的文章,大家可以先去看看那一篇再看這一篇,學習起來會更加得心應手
連結https://blog.csdn.net/pjh88/article/details/107574137

程式設計式事務控制物件

事務管理器:PlatformTransactionManager
PlatformTransactionManager是事務的管理器,他提供了我們常用的事務操作方法

在這裡插入圖片描述為什麼PlatformTransactionManager是介面型別?
因為不同的dao層技術有不同的實現類
Dao層是jdbc時:org.springframework.jdbc.datasource.DataSourceTransactionManager
Dao層是mybatis時:org.springframework.orm.hibernate5.HibernateTransactionManager

事務的定義資訊物件:TransactionDefinition
TransactionDefinition
裡面的方法

在這裡插入圖片描述
**

事務的隔離級別

**

ISOLATION_READ_UNCOMMITTED(讀未提交)

實質:一個事務讀取另一個事務未提交的資料

例子:老闆要給程式設計師發工資,程式設計師的工資是3.6萬/月。但是發工資時老闆不小心按錯了數字,按成3.9萬/月,該錢已經打到程式設計師的戶口,但是事務還沒有提交,就在這時,程式設計師去檢視自己這個月的工資,發現比往常多了3千元,以為漲工資了非常高興。但是老闆及時發現了不對,馬上回滾差點就提交了的事務,將數字改成3.6萬再提交。

分析:實際程式設計師這個月的工資還是3.6萬,但是程式設計師看到的是3.9萬。他看到的是老闆還沒提交事務時的資料。這就是髒讀。

ISOLATION_READ_COMMITTED(讀已提交)

實質:一個使用者讀取另一個使用者已提交的資料

事例:程式設計師拿著信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(程式設計師事務開啟),收費系統事先檢測到他的卡里有3.6萬,就在這個時候!!程式設計師的妻子要把錢全部轉出充當家用,並提交。當收費系統準備扣款時,再檢測卡里的金額,發現已經沒錢了(第二次檢測金額當然要等待妻子轉出金額事務提交完)。程式設計師就會很鬱悶,明明卡里是有錢的…

分析:這就是讀提交,若有事務對資料進行更新(UPDATE)操作時,讀操作事務要等待這個更新操作事務提交後才能讀取資料,可以解決髒讀問題。但在這個事例中,出現了一個事務範圍內兩個相同的查詢卻返回了不同資料,這就是不可重複讀。

ISOLATION_REPEATABLE_READ(重複讀)

實質:一個事務在讀取資料時,其他事務不允許進行修改操作

事例:程式設計師拿著信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(事務開啟,不允許其他事務的UPDATE修改操作),收費系統事先檢測到他的卡里有3.6萬。這個時候他的妻子不能轉出金額了。接下來收費系統就可以扣款了。

分析:重複讀可以解決不可重複讀問題。寫到這裡,應該明白的一點就是,不可重複讀對應的是修改,即UPDATE操作。但是可能還會有幻讀問題。因為幻讀問題對應的是插入INSERT操作,而不是UPDATE操作。

ISOLATION_SERIALIZABLE(幻讀)

事例:程式設計師某一天去消費,花了2千元,然後他的妻子去檢視他今天的消費記錄(全表掃描FTS,妻子事務開啟),看到確實是花了2千元,就在這個時候,程式設計師花了1萬買了一部電腦,即新增INSERT了一條消費記錄,並提交。當妻子列印程式設計師的消費記錄清單時(妻子事務提交),發現花了1.2萬元,似乎出現了幻覺,這就是幻讀。

**

事務的傳播行為

**

REQUIRED:如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。一般的選擇(預設值)
SUPPORTS:支援當前事務,如果當前沒有事務,就以非事務方式執行(沒有事務)
MANDATORY:使用當前的事務,如果當前沒有事務,就丟擲異常
REQUERS_NEW:新建事務,如果當前在事務中,把當前事務掛起。
NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起
NEVER:以非事務方式執行,如果當前存在事務,丟擲異常
NESTED:如果當前存在事務,則在巢狀事務內執行。如果當前沒有事務,則執行 REQUIRED 類似的操作
超時時間:預設值是-1,沒有超時限制。如果有,以秒為單位進行設定
是否只讀:建議查詢時設定為只讀

TransactionStatus:事務的具體執行狀態

TransactionStatus介面提供的是事務具體的執行狀態,方法如下

在這裡插入圖片描述
程式設計式事務控制的三大物件

PlatformTransactionManager
TransactionDefinition
TransactionStatus

基於XML的宣告式事務控制

Spring的宣告式事務控制顧名思義就是使用宣告的方式來處理事務,這裡的宣告指的是在配置檔案中宣告,Spring配置檔案中的宣告式處理來代替程式碼式的事務處理

宣告式事務處理的作用

事務處理是不侵入開發的元件,具體來說,業務邏輯物件不會意識帶正在處於事務處理之中,事實上也應該如此,因為事務管理是出於系統層面的職務,而不是業務邏輯處理的一部分,如果要改變事務管理策劃的話,也只需要在定義檔案中重新配置即可

在不需要事務管理的時候,只要在設定的檔案上修改一下,即可移除事務管理服務,不需要改變程式碼重新編譯,這樣維護起來更加方便

Spring事務控制的底層就是AOP

宣告式事務控制的實現

切點:需要被事務管理的方法,即業務方法

通知/增強:事務增強

切面:二者結合

下面通過一個銀行業務轉賬的案例來方便大家理解

1.建立資料庫和實體
表名:account
欄位名:moey--錢,Name--客戶名

CREATE TABLE account(
NAME VARCHAR(10),
money DOUBLE
);

插入三個欄位值

INSERT INTO account VALUE('tom',1),('bob',2),('jack',3);

在這裡插入圖片描述

2.需要匯入的座標

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring-mysql</artifactId>
    <version>1.0-SNAPSHOT</version>

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.32</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.0.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.0.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.13</version>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>RELEASE</version>
        <scope>compile</scope>
    </dependency>
</dependencies>
</project>

3.建立實體類

package com.pjh.account;

public class account {
    private  double money;
    private  String name;

    @Override
    public String toString() {
        return "account{" +
                "money=" + money +
                ", name='" + name + '\'' +
                '}';
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

4.dao層(執行語句,與資料庫互動)

介面

package com.pjh.dao;

public interface ServiceDao {
    public void inman(String inName,double money);
    public void outman(String outName,double money);
}

實現類

package com.pjh.dao.imp;
import com.pjh.dao.ServiceDao;
import org.springframework.jdbc.core.JdbcTemplate;

public class ServiceDaoImp implements ServiceDao {

    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void inman(String inName, double money) {
        jdbcTemplate.update("update account set money=money-? where name =?",money,inName);
    }
    public void outman(String outName, double money) {
          jdbcTemplate.update("update account set money=money+? where name =?",money,outName);
    }
}

5.service層
介面

package com.pjh.service;

public interface service {
    public void trasfer(String inName,String outName,double money);
}

實現類

package com.pjh.service.Imp;
import com.pjh.dao.imp.ServiceDaoImp;
import com.pjh.service.service;
import org.springframework.beans.factory.annotation.Autowired;

public class serviceImp implements service {

    private ServiceDaoImp serviceDaoImp;

    public void setServiceDaoImp(ServiceDaoImp serviceDaoImp) {
        this.serviceDaoImp = serviceDaoImp;
    }

    public void trasfer(String inName, String outName, double money) {
        serviceDaoImp.inman(inName,money);
  
        serviceDaoImp.outman(outName,money);
    }
}

6.applicationContext配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="password" value="${jdbc.password}"/>
    <property name="user" value="${jdbc.name}"/>
    <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
</bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
   <bean id="serviceDaoImp" class="com.pjh.dao.imp.ServiceDaoImp">
       <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>
    <!--目標物件 內部方法就是切點-->
    <bean id="serviceImp" class="com.pjh.service.Imp.serviceImp">
        <property name="serviceDaoImp" ref="serviceDaoImp"></property>
    </bean>
    <!--配置平臺事務管理器-->
    <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="*"/>
       </tx:attributes>
   </tx:advice>
    <!--配置事務的織入-->
  <aop:config proxy-target-class="true">
      <aop:pointcut id="txPointcut" expression="execution(* com.pjh.service.Imp.serviceImp.*(..))"/>
      <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
  </aop:config>
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <context:component-scan base-package="com.pjh"/>
</beans>

7.主函式

package com.pjh.control;
import com.pjh.service.Imp.serviceImp;
import com.pjh.service.service;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class control2 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
       service bean1 =(service) classPathXmlApplicationContext.getBean(service.class);
        bean1.trasfer("tom","jack",100);
    }
}

結果
成功轉賬
在這裡插入圖片描述

**

下面我們重點來講講切點方法的事務配置

**

<tx:advice id="txAdvice" transaction-manager="transactionManager">
       <tx:attributes>
           <tx:method name="*"/>
       </tx:attributes>
   </tx:advice>

<tx:method >:代表事務引數方法的配置
這個部分一定要重點掌握這是核心

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
name:切點方法名稱
isolation:事務的隔離級別
propogation:事務的傳播行為
timeout:超時時間
read-only:是否只讀

使用註解的方式進行事務的配置

1.dao層

package com.pjh.dao.imp;
import com.pjh.dao.ServiceDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("ServiceDaoImp")
public class ServiceDaoImp implements ServiceDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void inman(String inName, double money) {
        jdbcTemplate.update("update account set money=money-? where name =?",money,inName);
    }
    public void outman(String outName, double money) {
          jdbcTemplate.update("update account set money=money+? where name =?",money,outName);
    }
}

2.service層

package com.pjh.service.Imp;
import com.pjh.dao.imp.ServiceDaoImp;
import com.pjh.service.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("serviceImp")
@Transactional
public class serviceImp implements service {
    @Autowired
    private ServiceDaoImp serviceDaoImp;

 public void trasfer(String inName, String outName, double money) {
        serviceDaoImp.inman(inName,money);
        //int a=1/0;
        serviceDaoImp.outman(outName,money);
    }
}

3.編寫applicationContext的內容

<!--元件掃描-->
<context:component-scan base-package="com.pjh"/>
<!--事務的註解驅動-->
<tx:annotation-driven/>

小總結

1.使用 @Transactional 在需要進行事務控制的類或是方法上修飾,註解可用的屬性同 xml 配置方式,例如隔離級別、傳播行為等。
註解使用在類上,那麼該類下的所有方法都使用同一套註解引數配置。
使用在方法上,不同的方法可以採用不同的事務引數配置。
2.Xml配置檔案中要開啟事務的註解驅動<tx:annotation-driven />

以上就是Spring系列之事務的控制 註解實現+xml實現的知識點,如果各位覺得有幫助還請點贊關注支援作者,我也會在學習過程中不斷更新文章,目前主要是java開發和資料結構的一些知識,願我們一同進步,圓夢大廠,歡迎私信評論交流

在這裡插入圖片描述

相關文章