九、Spring 事务(两种配置方式)

一、概述

1.1 什么是事务

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)

1.2 事务的特性

  • 原子性(Atomicity)

一个事务中的所有操作不可分割,要么全部成功,要么全部失败

  • 一致性(Consistency)

一个事务执行完成前后,要保证事务前后业务逻辑的一致性

  • 隔离性(Isolation)

事务的执行不能受其他事务干扰,彼此隔离

  • 持久性(Durability)

事务的提交或者回滚都需要持久化到存储介质中

1.3 spring事务

spring对事务提供了两种实现方式:声明式事务和编程式事务管理;spring事务基于AOP实现

1.4 spring事务的隔离级别

  • 并发事务产生的问题
问题 描述 备注
脏读 一个事务对数据进行更新操作,另一个事务对数据进行读取,前者进行了回滚,后者读取到的数据便是脏数据,称之为脏读
不可重复读 一个事务对数据进行多次重复读取,另一个事务在读取期间对数据进行了更新操作,前者读取到的数据前后不一致,称之为不可重复读
幻读 一个事务对数据进行多次读取操作,期间另一个事务进行了插入数据的操作,前者读取数据前后不一致,称之为幻读
  • 事务的隔离级别及解决的问题
隔离级别 含义 脏读 不可重复读 幻读
READ UNCOMMITTED 读未提交
READ COMMITTED 读已提交
REPEATABLE READ 可重复读
SERIALIZABLE 序列化

1.5 spring事务的传播特性

传播行为 含义 备注
PROPAGATION_REQUIRED 表示当前方法必须运行在事务中,如果当前没有事务,就新建一个事务
PROPAGATION_SUPPORTS 如果当前有事务,就在事务中执行,如果当前没有事务,就以非事务方式执行
PROPAGATION_MANDATORY 表示当前方法必须在事务中运行,如果当前没有事务,就抛出异常
PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在自己的事务中,如果当前存在事务,把当前事务挂起
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常
PROPAGATION_NESTED 嵌套事务

二、xml配置实现

2.1 步骤

  1. 配置事务管理器
  2. 配置通知
  3. 配置切入点表达式
  4. 织入通知
  5. 配置事务属性

2.2 示例

beans.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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/tx
            https://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 配置service -->
    <bean id="userService" class="com.lizza.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

    <!-- 配置dao,由于dao继承了JdbcDaoSupport,所以需要初始化dataSource -->
    <bean id="userDao" class="com.lizza.dao.impl.UserDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置数据源,使用spring提供的dataSource:DriverManagerDataSource -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!-- spring基于xml声明式事务的配置 -->
    <!--
        1. 配置事务管理器
        2. 配置通知
        3. 配置切入点表达式
        4. 织入通知
        5. 配置事务属性
     -->
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 配置事务属性
                    name: 方法名
                    isolation: 隔离级别, 默认为DEFAULT, 为数据库的隔离级别
                    propagation: 事务的传播行为, 默认为REQUIRED, 表示一定会有事务; 涉及数据编辑操作选择REQUIRED, 涉及数据查询操作选择SUPPORTS
                    read-only: 是否只读事务, 默认值为false, 表示读写
                    timeout: 设置事务的超时时间, 默认值为-1, 表示永不超时
                    rollback-for: 指定一个异常, 发生该异常时, 事务进行回滚, 其他异常不回滚; 默认值为都需要回滚
                    no-rollback-for: 指定一个异常, 发生改一次, 事务不回滚, 其他异常回滚; 默认值为都需要回滚
             -->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="transfer*"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <!-- 配置切入点表达式 -->
        <aop:pointcut id="txPoint" expression="execution(* com.lizza.service.impl.*.*(..))"/>
        <!-- 织入通知 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint" ></aop:advisor>
    </aop:config>
</beans>

三、注解配置实现

3.1 关键注解

  • @EnableTransactionManagement:开启spring事务管理
  • @Transactional:指定需要运行在事务中的类或者方法

3.2 步骤

  1. 配置JdbcConfig
@Configuration
public class JdbcConfig {

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean("jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean("dataSource")
    public DataSource createDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}
  1. 配置TransactionConfig
@Configuration
public class TransactionConfig {

    /**
     * 配置事务管理器
     * @author: [email protected]
     * @date: 2020/4/6 10:54 下午
     * @param dataSource
     * @return org.springframework.transaction.PlatformTransactionManager
     */
    @Bean("transactionManager")
    public PlatformTransactionManager createPlatformTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
  1. 配置SpringConfig
@Configuration
@ComponentScan("com.lizza")
@Import({JdbcConfig.class, TransactionConfig.class})
@EnableTransactionManagement
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {

}
  1. 业务层配置
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public int addOne(User user) {
        return userDao.addOne(user);
    }

    @Override
    public int deleteOne(int id) {
        return userDao.deleteOne(id);
    }

    @Override
    public int updateOne(User user) {
        return userDao.updateOne(user);
    }

    @Override
    public User findOne(int id) {
        return userDao.findOne(id);
    }

    @Override
    public List<User> findAll() {
        return userDao.findAll();
    }

    @Override
    public int transferMoney(int from_id, int to_id, double money) {
        // 1. 获取付款账户和收款账户
        User from_user = userDao.findOne(from_id);
        User to_user = userDao.findOne(to_id);
        // 2. 付款账户付款,收款账户收款
        from_user.setMoney(from_user.getMoney() - money);
        to_user.setMoney(to_user.getMoney() + money);
        // 3. 更新账户信息
        userDao.updateOne(from_user);
        int i = 100/0;
        userDao.updateOne(to_user);
        return 0;
    }
}

源码地址:https://github.com/KJGManGlory/spring-framework

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章