MyBatis入门到精通——第五篇

第五篇:MyBatis事务管理与Spring的集成详解

在前面的文章中,我们学习了MyBatis的CRUD操作、动态SQL、分页以及缓存机制。本篇将深入探讨事务管理这一重要概念,并解释如何通过MyBatis与Spring框架集成来管理事务,确保数据操作的安全性和一致性。

1. 引言:为什么需要事务?
  • 事务的定义:事务(Transaction)是指一组操作,它们要么全部成功,要么全部失败。事务保证了数据库操作的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),这四个特性统称为ACID

  • 常见的事务场景:在实际开发中,有些操作是由多个步骤组成的,比如转账操作,涉及到从一个账户扣钱和向另一个账户存钱。只有这两步都成功,操作才能被视为完成。如果其中一个步骤失败,整个操作必须回滚,保证数据一致性。

  • 为什么需要事务管理:当项目中的数据库操作越来越复杂,尤其是涉及多个操作或数据库表时,事务管理显得尤为重要。它可以确保在出现错误时,数据库状态不会被破坏,避免部分操作成功而其他操作失败的情况。

2. MyBatis中的事务管理

MyBatis提供了简单的事务管理机制,事务的管理可以通过两种方式实现:

  1. JDBC事务:通过SqlSession的手动提交或回滚来管理事务。
  2. Spring事务:通过Spring框架的事务管理器,自动处理事务的开启、提交和回滚。
2.1 手动管理事务(JDBC事务)

MyBatis通过SqlSession来管理数据库操作和事务。在默认情况下,SqlSession是自动提交的,但我们也可以手动管理事务的提交和回滚。

步骤1:手动提交事务

  • 当我们使用SqlSession时,可以控制事务的提交和回滚。

  • 示例代码

    SqlSession session = sqlSessionFactory.openSession();
    try {
        // 执行数据库操作
        User user = new User();
        user.setName("John");
        user.setAge(30);
        session.insert("com.example.mapper.UserMapper.insertUser", user);
        
        // 手动提交事务
        session.commit();
    } catch (Exception e) {
        // 出现异常时,回滚事务
        session.rollback();
    } finally {
        // 关闭SqlSession
        session.close();
    }
    
  • 解释

    • session.commit():提交事务,将操作应用到数据库。
    • session.rollback():在异常发生时回滚事务,撤销已执行的操作,确保数据一致性。

手动事务管理虽然提供了精细的控制,但在复杂的项目中,每次都需要手动管理事务显得冗繁。因此,通常推荐使用Spring的自动事务管理来简化操作。

2.2 自动管理事务(Spring事务)

Spring提供了更高级的事务管理机制,它通过声明式事务管理的方式,简化了开发者的工作,不需要手动管理commitrollback,只需通过注解或配置指定事务的边界即可。

3. Spring事务管理与MyBatis的集成

MyBatis与Spring的集成使得我们能够利用Spring强大的事务管理机制,自动管理事务的开启、提交和回滚。下面我们将讲解如何通过Spring来集成MyBatis的事务管理。

3.1 项目依赖

pom.xml中确保添加了Spring和MyBatis相关的依赖:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
3.2 配置Spring事务管理器

Spring的DataSourceTransactionManager用于管理数据库事务。在Spring Boot项目中,我们只需要进行最小化的配置即可。

步骤1:配置数据库和事务管理器

  • application.yml中配置数据源:
    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/mydb
        username: root
        password: password
        driver-class-name: com.mysql.cj.jdbc.Driver
    

步骤2:启用事务支持

  • 在Spring Boot应用的启动类上使用@EnableTransactionManagement注解,启用事务管理。
    @SpringBootApplication
    @EnableTransactionManagement
    public class MyBatisApp {
        public static void main(String[] args) {
            SpringApplication.run(MyBatisApp.class, args);
        }
    }
    

步骤3:事务管理配置类

  • 我们可以通过创建配置类,手动定义事务管理器,尽管Spring Boot会自动配置它。
    @Configuration
    public class TransactionConfig {
        @Bean
        public DataSourceTransactionManager transactionManager(DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    }
    
3.3 使用注解管理事务

Spring提供了@Transactional注解来声明事务,开发者只需在需要事务管理的方法上加上这个注解,Spring会自动处理事务的开启、提交和回滚。

示例

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Transactional
    public void createUser(User user) {
        userMapper.insertUser(user);
        // 模拟业务逻辑中的异常
        if (user.getAge() < 18) {
            throw new RuntimeException("Age must be greater than 18");
        }
    }
}

解释

  • @Transactional:声明这个方法是一个事务操作,Spring会在方法开始时开启事务,方法执行结束时提交事务。如果发生异常,Spring会自动回滚事务。
3.4 @Transactional的属性

@Transactional注解提供了许多有用的属性,帮助我们定制事务的行为。

  • propagation(传播行为):定义事务传播的方式,即在调用多个事务时,如何管理这些事务。

    • REQUIRED(默认):当前没有事务就创建一个新的,有的话就加入当前事务。
    • REQUIRES_NEW:总是创建一个新的事务,暂停当前事务。
    • SUPPORTS:如果当前有事务就加入,没有就不创建新的事务。
  • isolation(隔离级别):定义事务的隔离级别,影响事务之间的数据可见性。

    • READ_UNCOMMITTED:允许事务读取未提交的数据,可能导致脏读。
    • READ_COMMITTED:事务只能读取已提交的数据。
    • REPEATABLE_READ:保证在一个事务中多次读取相同数据时,结果一致。
    • SERIALIZABLE:最高的隔离级别,确保事务严格按照顺序执行,但性能较低。
  • timeout:指定事务的超时时间,默认没有超时设置。

  • readOnly:标记事务为只读事务,优化性能,通常用于只执行查询的场景。

示例

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, timeout = 30, readOnly = false)
public void updateUser(User user) {
    userMapper.updateUser(user);
}

解释

  • propagation = Propagation.REQUIRED:使用默认传播行为,当前有事务就加入,没有就创建一个新的事务。
  • isolation = Isolation.READ_COMMITTED:防止脏读,只能读取已提交的数据。
  • timeout = 30:事务执行时间超过30秒就自动回滚。
  • readOnly = false:此事务允许进行更新操作。
4. 事务回滚的细节
  • 默认回滚行为:Spring默认会在遇到RuntimeException或其子类时回滚事务,而对于CheckedException(非运行时异常),则不会自动回滚。
  • 手动指定回滚异常:我们可以通过@Transactional注解指定在遇到哪些异常时回滚事务。

示例

@Transactional(rollbackFor = Exception.class)
public void createUser(User user) throws Exception {
    userMapper

.insertUser(user);
    // 手动抛出异常,回滚事务
    if (user.getAge() < 18) {
        throw new Exception("User is underage");
    }
}

解释

  • rollbackFor = Exception.class:指定当抛出Exception或其子类时,回滚事务。
5. 综合示例:转账事务管理
  • 场景:假设我们在一个银行系统中进行转账操作,转账操作包括从一个账户扣款和向另一个账户存款。这两步操作必须保证同时成功或者同时失败。

示例代码

@Service
public class AccountService {

    @Autowired
    private AccountMapper accountMapper;

    @Transactional
    public void transfer(int fromAccountId, int toAccountId, double amount) {
        // 从账户A扣款
        accountMapper.debit(fromAccountId, amount);
        // 向账户B存款
        accountMapper.credit(toAccountId, amount);
        // 模拟异常
        if (toAccountId == 0) {
            throw new RuntimeException("Invalid target account");
        }
    }
}

解释

  • debit()credit() 方法分别执行扣款和存款操作。通过@Transactional注解,保证如果出现异常,整个操作将被回滚,确保数据一致性。
6. 总结与展望
  • 总结:通过本篇文章,我们学习了MyBatis中的事务管理机制,尤其是如何与Spring事务管理集成。事务管理是确保数据一致性的重要机制,尤其在复杂操作中尤为关键。Spring提供了强大的事务管理工具,通过简单的注解即可完成复杂的事务管理逻辑。
  • 预告下一篇文章:在下一篇文章中,我们将探讨MyBatis与多数据源的集成,学习如何在一个项目中同时操作多个数据库,进一步提升项目的灵活性和扩展性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值