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