Spring(七)-事务操作

一、事务相关概念

1)事务管理是企业级应用程序开发中必不可少的技术,  用来确保数据的完整性和一致性.

2)事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用

3)事务的四个关键属性(ACID)

  • 原子性(atomicity): 事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用.
  • 一致性(consistency): 一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中.
  • 隔离性(isolation): 可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏.
  • 持久性(durability): 一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中.

二、Spring事务管理

2.1、Spring事务介绍

1)事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)

2)在 Spring 进行事务管理操作有两种方式

  • 编程式事务管理
  • 声明式事务管理(使用)

3)声明式事务管理

  • 基于注解方式(使用)
  • 基于 xml 配置文件方式

4)在 Spring 进行声明式事务管理,底层使用 AOP 原理

5)Spring 事务管理 API

提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类

image

2.2、注解声明式事务管理

以转账为例:

1)Dao方法:

UserDao:

package com.dianchou.spring.tx;

/**
 * @author lawrence
 * @create 2020-07-09 11:11
 */
public interface UserDao {

    //账户转出
    public void reduceBalance();

    //账户转入
    public void addBalance();
}

UserDaoImpl:

package com.dianchou.spring.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

/**
 * @author lawrence
 * @create 2020-07-09 11:11
 */

@Repository
public class UserDaoImpl implements UserDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void reduceBalance() {
        String sql = "update account set balance=balance-? where username = ?";
        jdbcTemplate.update(sql,100,"Tom");
    }

    @Override
    public void addBalance() {
        String sql = "update account set balance=balance+? where username = ?";
        jdbcTemplate.update(sql,100,"Jerry");
    }
}

2)Service方法

package com.dianchou.spring.tx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author lawrence
 * @create 2020-07-09 11:10
 */

//@Transactional,
//(1)这个注解添加到类上面,也可以添加方法上面
//(2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务
//(3)如果把这个注解添加方法上面,为这个方法添加事务
@Service
@Transactional
public class UserService {

    @Autowired
    private UserDao userDao;

    public void accountBalance(){
        userDao.reduceBalance();
//        int i = 10 / 0;
        userDao.addBalance();
    }
}

3)配置文件

<?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:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!--开启组件扫描-->
    <context:component-scan base-package="com.dianchou.spring.tx"></context:component-scan>

    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${prop.url}" />
        <property name="username" value="${prop.userName}" />
        <property name="password" value="${prop.password}" />
        <property name="driverClassName" value="${prop.driverClass}" />
    </bean>

    <!--配置JdbcTemplate对象并注入datasource-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>

4)测试

@Test
public void testTX(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.accountBalance();
}

2.3、事务配置相关参数

在 service 类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数

image

2.3.1、propagation:事务传播行为

当事务方法被另一个事务方法调用时, 必须指定事务应该如何传播. 例如: 方法可能继续在现有事务中运行, 也可能开启一个新事务, 并在自己的事务中运行.

事务的传播行为可以由传播属性指定. Spring 定义了 7  种类传播行为.

image

image

2.3.2、ioslation:事务隔离级别

关于三个读问题:

  1. 脏读:一个未提交事务读取到另一个未提交事务的数据
  2. 不可重复读:一个未提交事务读取到另一提交事务修改数据
  3. 虚读:一个未提交事务读取到另一提交事务添加数据

解决:通过设置事务隔离级别,解决读问题

image

image

2.3.3、timeout:超时时间

(1)事务需要在一定时间内进行提交,如果不提交进行回滚

(2)默认值是 -1 ,设置时间以秒单位进行计算

2.3.4、readOnly:是否只读

(1)读:查询操作,写:添加修改删除操作

(2)readOnly 默认值 false,表示可以查询,可以添加修改删除操作

(3)设置 readOnly 值是 true,设置成 true 之后,只能查询

2.3.5、rollbackFor:回滚

(1)设置出现哪些异常进行事务回滚

2.3.6、noRollbackFor:不回滚

(1)设置出现哪些异常不进行事务回滚

2.4、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">

    <!--引入外部资源-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!--开启组件扫描-->
    <context:component-scan base-package="com.dianchou.spring.tx"></context:component-scan>

    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${prop.url}" />
        <property name="username" value="${prop.userName}" />
        <property name="password" value="${prop.password}" />
        <property name="driverClassName" value="${prop.driverClass}" />
    </bean>

    <!--配置JdbcTemplate对象并注入datasource-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置通知-->
    <tx:advice id="txadvice">
        <!--配置事务参数-->
        <tx:attributes>
            <!--指定哪种规则的方法上面添加事务-->
            <tx:method name="accountBalance" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--配置切入点和切面-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="pt" expression="execution(* com.dianchou.spring.tx.UserService.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>

    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>

2.5、完全注解声明式事务管理

package com.dianchou.spring.tx;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;

/**
 * @author lawrence
 * @create 2020-07-09 16:08
 */

@Configuration  //配置类
@ComponentScan(basePackages = {"com.dianchou.spring.tx"})
@EnableTransactionManagement
public class TxConfig {

    /**
     * 创建数据库连接池
     */
    @Bean
    public DruidDataSource getDruidDataSource() throws IOException {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://47.110.61.153:3306/mybatis");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        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) {
        DataSourceTransactionManager transactionManager = new
                DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}
原文地址:https://www.cnblogs.com/hujinzhong/p/13272361.html