Spring 事物

本文深入探讨Spring框架中的事务管理机制,包括ACID特性、并发问题与隔离级别,同时详细讲解如何使用JdbcTemplate进行数据库操作,涵盖纯注解与XML配置两种方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 事物

事务:指的是逻辑上一组操作,组成这个事务的各个执行单元,要么一起成功,要么一起失败!

1.1 事务的特性(ACID)

原子性(Atomicity)

原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。

一致性(Consistency)

一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

隔离性(Isolation)

隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

持久性(Durability)

持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作

1.2 事物并发可能的问题

脏读:一个事务读取到另一个事务未提交的数据。
不可重复读:一个事务因读取到另一个事务已提交的数据。导致对同一条记录读取两次以上的结果不一致。update操作
幻读:一个事务因读取到另一个事务已提交的数据。导致对同一张表读取两次以上的结果不一致。insert、delete操作

1.3 事物隔离级别

① Read uncommitted (读未提交):最低级别,任何情况都无法保证。
② Read committed (读已提交):可避免脏读的发生。Oracle、DB2默认隔离级别
③ Repeatable read (可重复读):可避免脏读、不可重复读的发生。mysql默认隔离级别
④ Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

2 spring事物

Spring事务管理器的接口是PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器
在这里插入图片描述

  1. PlatformTransactionManager接口 – 平台事务管理器.(真正管理事务的类)。该接口有具体的实现类,根据不同的持久层框架,需要选择不同的实现类!
  2. TransactionDefinition接口 – 事务定义信息.(事务的隔离级别,传播行为,超时,只读)
  3. TransactionStatus接口 – 事务的状态(是否新事务、是否已提交、是否有保存点、是否回滚)
  4. 总结:上述对象之间的关系:平台事务管理器真正管理事务对象.根据事务定义的信息TransactionDefinition 进行事务管理,在管理事务中产生一些状态.将状态记录到TransactionStatus中

底层采用AOP的技术

3 spring管理Jdbc Template

3.1 导入相关依赖

<!-- 省略spring基本依赖,core,beans.context,
<!-- spring 事物 -->
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-tx</artifactId>
     <version>${spring-version}</version>
 </dependency>
 <!-- spring jdbc -->
 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>${spring-version}</version>
 </dependency>

3.2 纯注解形式

零xml配置

3.2.1 db.properties和ProertiesConfig

db.properties

db.driver=com.mysql.jdbc.Driver
db.url = jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8
db.username = root
db.password=root
@Configuration
@PropertySource("classpath:db.properties")
public class ProertiesConfig {
    @Value("${db.driver}")
    private String DRIVER;
    @Value("${db.url}")
    private String URL;
    @Value("${db.username}")
    private String USERNAME;
    @Value("${db.password}")
    private String PASSWORD;

    @Bean("dataSource")
    public DataSource dataSource(){
    	// spring内置连接池
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(DRIVER);
        dataSource.setUrl(URL);
        dataSource.setUsername(USERNAME);
        dataSource.setPassword(PASSWORD);
        return dataSource;
    }
}

3.2.2 DBConnectConfig

@Configuration
// 开启事物管理器
@EnableTransactionManagement
// 导入ProertiesConfig 配置
@Import(ProertiesConfig.class)
public class DBConnectConfig {
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

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

3.2.3 SpringConfiguration

// 全局配置文件
@Configuration
@ComponentScan(basePackages = "com.cc")
@Import(DBConnectConfig.class)
@EnableAspectJAutoProxy
public class SpringConfiguration {
    //spring容器初始化时,会调用配置类的无参构造函数
    public SpringConfiguration(){
        System.out.println("容器启动初始化");
    }
}

3.2.4 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
// 开启事物
@Transactional
// 如果不加@Rollback(false)则insert update 会一直失败
@Rollback(false)
public class JdbcTempleTest {
    @Autowired
    JdbcTemplate jdbcTemplate;
    private  int threadNum;

    @Test
    public void testInsert(){
        System.out.println("第"+ threadNum +" thread");
        String sql ="INSERT INTO user(username,password,email) VALUES(?,?,?)";
        try {
            int count = jdbcTemplate.update(sql, "ceshi Temple"+threadNum, "1111", "test@com");
            System.out.println(count);
        }catch (Throwable t){
            t.printStackTrace();
        }
    }
    @Test
    public void testQuery(){
        System.out.println("第"+ threadNum +" thread");
        String sql ="SELECT * FROM user";
        List<Map<String, Object>> users = jdbcTemplate.queryForList(sql);
        users.stream().forEach((x)->System.out.println(threadNum +" "+x));
    }
    @Test
    public void testThread(){
        for (int i =0;i<5;i++){
            threadNum =i+1;
            Runnable r1 = ()->testInsert();
            r1.run();
            Runnable r2 = ()->testQuery();
            r2.run();
        }
    }
}

3.3 xml配置连接池+注解 形式

上面的demo中采用spring内置连接池管理datasource,这次使用c3p0连接池,导入相关依赖

<dependency>
    <groupId>c3p0</groupId>
     <artifactId>c3p0</artifactId>
     <version>0.9.1.2</version>
 </dependency>

3.3.1 applicationContext.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: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/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
">
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.cc"/>
    <context:property-placeholder location="classpath:db.properties"/>
	<!-- 创建连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${db.driver}"/>
        <property name="jdbcUrl" value="${db.url}"/>
        <property name="user" value="${db.username}"/>
        <property name="password" value="${db.username}"/>
    </bean>
	<!-- transactionManager 事物管理器管理连接池 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
	<!-- 开启事物 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

3.3.2 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
@Transactional
public class JdbcTempleXmlTest {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testInsert(){
        String sql ="INSERT INTO user(username,password,email) VALUES(?,?,?)";
        try {
            int count = jdbcTemplate.update(sql, "ceshi XML", "1111", "test@com");
            System.out.println(count);
        }catch (Throwable t){
            t.printStackTrace();
        }
    }

    @Test
    public void testQuery(){
        String sql ="SELECT * FROM user";
        List<Map<String, Object>> users = jdbcTemplate.queryForList(sql);
        users.stream().forEach((x)->System.out.println(x));
    }

}
### Spring事务管理配置与使用示例 Spring 事务管理通过声明式事务控制来简化开发,减少手动管理事务的复杂性。以下是一个完整的事务管理配置和使用的示例。 #### 配置事务管理器 在基于 XML 的配置中,可以使用 `JpaTransactionManager` 或 `DataSourceTransactionManager` 来管理事务。以下是使用 `DataSourceTransactionManager` 的配置示例[^5]: ```xml <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 开启事务注解支持 --> <tx:annotation-driven transaction-manager="transactionManager"/> ``` 如果使用 JPA,则可以配置 `JpaTransactionManager`: ```xml <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <!-- 开启事务注解支持 --> <tx:annotation-driven transaction-manager="transactionManager"/> ``` #### 使用 @Transactional 注解 在服务层方法上使用 `@Transactional` 注解来定义事务行为。以下是一个简单的事务管理示例[^4]: ```java @Service public class UserService { @Autowired private UserDao userDao; @Transactional public void createUser(User user) { userDao.createUser(user); if ("error".equals(user.getUsername())) { throw new RuntimeException("Simulated exception for rollback"); } } @Transactional public void updateUser(User user) { userDao.updateUser(user); if ("error".equals(user.getUsername())) { throw new RuntimeException("Simulated exception for rollback"); } } } ``` #### 事务传播行为 事务传播行为决定了当前事务与新事务之间的关系。例如,`Propagation.REQUIRED` 是默认值,表示如果有现有事务则加入该事务,否则创建一个新事务[^1]。 ```java @Transactional(propagation = Propagation.REQUIRED) public void requiredTransaction() { // 业务逻辑 } @Transactional(propagation = Propagation.REQUIRES_NEW) public void requiresNewTransaction() { // 新事务独立运行 } ``` #### 注意事项 1. **内部调用问题**:在同一类中调用被 `@Transactional` 注解的方法时,事务可能不会生效。这是因为 Spring AOP 动态代理无法拦截类内部的方法调用[^3]。 2. **异常处理**:只有未捕获的受检异常或运行时异常才会触发回滚,默认情况下,已捕获的异常不会导致事务回滚[^2]。 3. **只读事务**:对于只读操作,可以通过设置 `readOnly=true` 提高性能,避免不必要的锁[^1]。 ```java @Transactional(readOnly = true) public List<User> findAllUsers() { return userDao.findAll(); } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值