Spring 数据访问事务管理例子

作者: 右耳菌 | 来源:发表于2022-04-15 00:33 被阅读0次

以下内容均可参考官网


一、Spring声明式事务实现关键要点

1. 创建一个maven项目
2. 修改pom.xml
<project
        xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>cn.lazyfennec</groupId>
    <artifactId>SpringTransaction</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.9.1</version>
            <scope>runtime</scope>
        </dependency>
        <!-- MySQL驱动  -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
        <!-- 数据库连接池  -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.9.0</version>
        </dependency>
    </dependencies>
</project>
3. 新建db.properties,在resources目录下
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy?serverTimezone=GMT(数据库名称记得替换成你的)
jdbc.username=#### (替换成你的数据库账号)
jdbc.password=#### (替换成你的数据库密码)
4. 新建application-context.xml
  • 主要记得添加schema

xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"

  • 具体文件如下
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <context:component-scan base-package="cn.lazyfennec"/>
    
    <!-- 数据源配置 -->
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    
    <context:property-placeholder location="db.properties"/>
</beans>
5. 定义事务管理器
  • 在application-context.xml 中加入以下内容
    <!-- 定义事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
6. 定义事务Advice
  • 在application-context.xml 中加入以下内容
    <!-- 定义事务 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!-- 表示匹配get开头的方法,*表示匹配任意内容, read-only="true"表示只读事务-->
            <tx:method name="get*" read-only="true"/>
            <!--表示匹配任意方法-->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
7. 定义Pointcut
  • 在application-context.xml 中加入以下内容
    <!-- 定义Pointcut -->
    <aop:config>
        <aop:pointcut id="daoOperation" expression="execution(* cn.lazyfennec.dao.AccountDao.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="daoOperation"/>
    </aop:config>
8. 配置<tx:method />,参考 “6. 定义事务Advice
  • name:匹配的函数名称,支持*匹配
  • propagation:事务传播行为
  • isolation:事务隔离级别
  • timeout:超时
  • read-only:是否只读事务
  • rollback-for:触发回滚的异常,逗号分隔
  • no-rollback-for:不触发回滚的异常,逗号分隔
9. 开启注解方式@Transactional
<!-- 开启注解 -->
<tx:annotaion-driven transaction-manager="txManager" />
  • value:
  • value:使用的TransactionManager
  • propagation:事务传播行为
  • isolation:事务隔离级别
  • timeout:超时
  • readOnly:是否只读事务
  • rollbackFor:触发回滚的异常类对象数组
  • rollbackForClassName:触发回滚的异常类名称数组
  • noRollbackFor:不触发回滚的异常类对象数组
  • noRollbackForClassName:不触发回滚的异常类名称数组
10. 创建Account,此时未添加事务
package cn.lazyfennec.entity;

/**
 * @Author: Neco
 * @Description:
 * @Date: create in 2022/4/14 23:59
 */
public class Account {
    private int id;
    private String name;
    private Double money;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

11. 创建AccoutDao
package cn.lazyfennec.dao;

import cn.lazyfennec.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

/**
 * @Author: Neco
 * @Description:
 * @Date: create in 2022/4/14 23:54
 */
@Repository
public class AccountDao {

    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    /**
     * 重置所有账户余额为1000元
     */
    public void resetMoney() {
        jdbcTemplate.update("update account set money = 1000");
    }

    /**
     * 获取所有账户信息
     *
     * @return
     */
    public List<Account> listAccount() {
        return this.jdbcTemplate.query("select * from account", new RowMapper<Account>() {
            public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
                Account account = new Account();
                account.setId(rs.getInt("id"));
                account.setName(rs.getString("name"));
                account.setMoney(rs.getDouble("money"));
                return account;
            }
        });
    }

    /**
     * 转账
     *
     * @param source
     * @param target
     * @param money
     */
    public void transferMoney(String source, String target, double money) {
        this.jdbcTemplate.update("update account set money=money-? where name=?", money, source);
        throwException();
        this.jdbcTemplate.update("update account set money=money+? where name=?", money, target);
    }

      /**
     * 模拟抛出异常
     */
    private void throwException() {
        throw new RuntimeException("ERROR");
    }
}

12. 创建启动类
package cn.lazyfennec;

import cn.lazyfennec.dao.AccountDao;
import cn.lazyfennec.entity.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

/**
 * @Author: Neco
 * @Description:
 * @Date: create in 2022/4/15 0:01
 */
public class SpringTransaction {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        AccountDao dao = context.getBean("accountDao", AccountDao.class);
        dao.resetMoney();

        List<Account> accounts = dao.listAccount();
        for(Account account : accounts) {
            System.out.println(account);
        }
        try {
            dao.transferMoney("aaa", "bbb", 200);
        } catch (Exception e) {
            System.out.println(e.getMessage()); // 触发异常,自动回滚
        }
        System.out.println("========================================");
        accounts = dao.listAccount();
        for(Account account : accounts) {
            System.out.println(account);
        }
        ((ConfigurableApplicationContext) context).close();
    }

}

13.如果使用注解@Transactional
  • 首先application-context.xml中要加入以下内容
    <!-- 开启注解 -->
    <tx:annotation-driven transaction-manager="txManager"/>
  • 此时,以下的内容可以删除
<!-- 定义事务 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!-- 表示匹配get开头的方法,*表示匹配任意内容, read-only="true"表示只读事务-->
            <tx:method name="get*" read-only="true"/>
            <!--表示匹配任意方法-->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!-- 定义Pointcut -->
    <aop:config>
        <aop:pointcut id="daoOperation" expression="execution(* cn.lazyfennec.dao.AccountDao.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="daoOperation"/>
    </aop:config>
  • 修改AccountDao的transferMoney 如下所示:
    /**
     * 转账
     *
     * @param source
     * @param target
     * @param money
     */
    @Transactional(propagation = Propagation.REQUIRED) // 也可以使用默认
    public void transferMoney(String source, String target, double money) {
        this.jdbcTemplate.update("update account set money=money-? where name=?", money, source);
        throwException();
        this.jdbcTemplate.update("update account set money=money+? where name=?", money, target);
    }
14. 执行结果如下所示
Account{id=1, name='aaa', money=1000.0}
Account{id=2, name='bbb', money=1000.0}
Account{id=3, name='ccc', money=1000.0}
Account{id=6, name='fff', money=1000.0}
Account{id=8, name='eee', money=1000.0}
ERROR
========================================
Account{id=1, name='aaa', money=1000.0}
Account{id=2, name='bbb', money=1000.0}
Account{id=3, name='ccc', money=1000.0}
Account{id=6, name='fff', money=1000.0}
Account{id=8, name='eee', money=1000.0}

Process finished with exit code 0

二、编程式事务

  • TransactionTemplate
  • PlatformTransactionManager的实现
  • 定义TransactionTemplate
定义TransactionTemplate
  • 使用TransactionTemplate


    使用TransactionTemplate
    使用TransactionTemplate
    使用TransactionTemplate
  • PlatformTransactionManager的实现


    PlatformTransactionManager的实现

三、如何选择声明式事务和编程式事务

如何选择声明式事务和编程式事务

相关文章

网友评论

    本文标题:Spring 数据访问事务管理例子

    本文链接:https://www.haomeiwen.com/subject/lygxertx.html