1. 程式人生 > 其它 >Spring5學習筆記(八)——事務操作

Spring5學習筆記(八)——事務操作

技術標籤:Springspring

Spring5學習筆記(八)——事務操作

尚矽谷-Spring5框架2020最新版教程(idea版)

https://www.bilibili.com/video/BV1Vf4y127N5?p=1


文章目錄

一、事務概念

二、搭建事務操作環境

三、事務操作

1. 事務操作基本過程

2.Spring事務管理介紹

3. 宣告式事務管理(基於註解方式)

4.事務引數配置

(1)事務傳播行為(propagation)

(2)事務隔離級別(isolation)

(3)其它

5. 宣告式事務管理(基於xml配置方式)(瞭解)

6.宣告式事務管理(完全註解開發)


一、事務概念

事務:事務是資料庫操作最基本單元,是邏輯上的一組操作。
典型場景:銀行轉賬。
事務的四個特性(ACID):原子性、一致性、隔離性、永續性。

二、搭建事務操作環境

以lucy向mary轉賬100為例。

1.建立資料庫和表,新增記錄

建立t_count表並插入資料。


2.建立service,搭建dao,完成物件建立和注入。(在service中注入dao,在dao中注入JdbcTemplate,在JdbcTemplate中注入DataSource)
3.Dao層:建立兩個方法,轉出者減少錢,轉入者增加錢。
Service層:建立轉賬方法(呼叫dao的方法)。

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

    <!--開啟元件掃描-->
    <context:component-scan base-package="com.smq.spring5"/>
    <!--資料庫連線池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/user_db?useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>
    <!--JdbcTemplate物件-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入dataSource-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>
public interface UserDao {
    public void addMoney();
    public void reduceMoney();
}

@Repository
public class UserDaoImpl implements UserDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;
    //lucy轉賬100給mary,mary增加100
    @Override
    public void addMoney() {
        String sql = "update t_account set money=money+? where username=?";
        jdbcTemplate.update(sql,100, "mary");
    }
    //lucy轉賬100給mary,lucy減少100
    @Override
    public void reduceMoney() {
        String sql = "update t_account set money=money-? where username=?";
        jdbcTemplate.update(sql,100, "lucy");
    }
}

@Service
public class UserService {
    //注入dao
    @Autowired
    private UserDao userDao;
    //轉賬方法
    public void accountMoney(){
        userDao.reduceMoney();//lucy減少100
        userDao.addMoney();//mary增加100
    }
}

4.測試。

    @Test
    public void testAccount(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }

注:異常情況!!!

若將UserService中的方法改為:

    //轉賬方法
    public void accountMoney(){
        userDao.reduceMoney();//lucy減少100
        int i = 10 / 0;
        userDao.addMoney();//mary增加100
    }

三、事務操作

1. 事務操作基本過程

(1)開啟事務;
(2)進行業務操作;
(3)若沒異常提交事務;
(4)若有異常回滾事務。

2.Spring事務管理介紹

(1)事務一般新增到Service層(業務邏輯層)。
(2)在Spring進行事務管理操作的方式:程式設計式事務管理宣告式事務管理(常用)
(3)宣告式事務管理:基於註解方式(常用)和基於xml配置檔案方式。
(4)在Spring進行宣告式事務管理,底層使用AOP管理
(5)Spring事務管理提供一個介面PlatformTransactionManager,代表事務管理器,這個介面針對不同的框架提供不同的實現類。

3. 宣告式事務管理(基於註解方式)

基本步驟:

  1. 在spring配置檔案中配置事務管理器;
  2. 在spring配置檔案中開啟事務註解:先引入名稱空間tx,再開啟事務註解(使用tx:annotation-driven標籤);
  3. 在service類上(或service類裡面的方法上)新增事務註解@Transactional。
<?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">
    <!--開啟元件掃描-->
    <context:component-scan base-package="com.smq.spring5"/>
    <!--資料庫連線池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/user_db?useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>
    <!--JdbcTemplate物件-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入dataSource-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--建立事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入資料來源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--開啟事務註解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

UserService新增事務註解:

@Service
@Transactional
public class UserService {
    //注入dao
    @Autowired
    private UserDao userDao;
    //轉賬方法
    public void accountMoney(){
        userDao.reduceMoney();//lucy減少100
        int i = 10 / 0;
        userDao.addMoney();//mary增加100
    }
}

結果:資料庫不發生變化,發生異常時事務回滾。


4.事務引數配置

可在@Transactional註解中配置事務相關引數。常用引數有:

  • propagation:事務傳播行為。
  • isolation:事務隔離級別。
  • timeout:超時時間。
  • readOnly:是否只讀。
  • rollbackFor:回滾。
  • noRollbackFor:不回滾。

(1)事務傳播行為(propagation)

多事務方法(對資料庫表資料進行變化的操作,如增刪改)直接進行呼叫,這個過程中事務是如何管理的。以add方法呼叫update方法為例。
Spring框架事務傳播行為有7種(常用兩種):

  • REQUIRED(預設):如果有事務在執行,當前的方法就在這個事務內執行;否則,就啟動一個新的事務,並在自己的事務內執行。若add方法本身有事務,呼叫update方法後,update使用當前add方法裡的事務。若add方法本身沒事務,呼叫update方法後,建立新事務。
  • REQUIRED_NEW:當前的方法必須啟動新事務,並在它自己的事務內執行,若有事務正在執行則將其掛起。使用add方法呼叫update方法,無論add是否有事務,都建立新的事務。

(2)事務隔離級別(isolation)

在多事務操作間不會產生影響即隔離性。若不考慮隔離性會產生三個問題:

  • 髒讀:一個未提交事務讀取到另一個未提交事務的資料。
  • 不可重複讀:一個未提交事務讀取到另一提交事務修改的資料。
  • 虛(幻)讀:一個未提交事務讀取到另一提交事務新增的資料。

通過設定事務隔離性解決讀問題。

  • READ UNCOMMITTED(讀未提交):有髒讀、有不可重複讀、有幻讀。
  • READ COMMITTED(讀已提交):無髒讀、有不可重複讀、有幻讀。
  • REPEATABLE READ(可重複讀)(預設):無髒讀、無不可重複讀、有幻讀。
  • SERIALIZABLE(序列化):無髒讀、無不可重複讀、無幻讀。

(3)其它

timeout:超時時間。事務需要在一定時間內提交,若超時則回滾,預設-1,設定時間以秒為單位。
readOnly:是否只讀。讀:查詢。寫:新增、修改、刪除。readOnly預設值為false,表示可以查詢,也可以新增、修改、刪除。設定為true後表示只能查詢。
rollbackFor:回滾。設定出現哪些異常進行事務回滾。
noRollbackFor:不回滾。設定出現哪些異常不進行事務回滾。


5. 宣告式事務管理(基於xml配置方式)(瞭解)

步驟:

  1. 配置事務管理器;
  2. 配置通知;
  3. 配置切入點和切面。
    <!--1. 建立事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入資料來源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--2. 配置通知-->
    <tx:advice id="txadvice">
        <!--配置事務引數-->
        <tx:attributes>
            <!--指定在哪種規則的方法上新增事務-->
            <tx:method name="accountMoney" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--3. 配置切入點和切面-->
    <aop:config>
        <!--配置切入點-->
        <aop:pointcut id="pt" expression="execution(* com.smq.spring5.service.UserService.*(..)"/>
        <!--配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    </aop:config>

6.宣告式事務管理(完全註解開發)

建立配置類(不需要配置檔案):

@Configuration//配置類
@ComponentScan(basePackages = "com.smq.spring5")//開啟元件掃描
@EnableTransactionManagement//開啟事務
public class TxConfig {
    //建立資料庫連線池
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=utf8");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
    //建立JdbcTemplate物件
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        //到ioc容器中根據型別找到dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //注入dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
    //建立事務管理器物件
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        //到ioc容器中根據型別找到dataSource
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        //注入dataSource
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

測試類需要修改!!!

    @Test
    public void testAccount(){
        ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }