Spring框架 - 声明式事务管理

Spring声明式事务详解

Spring框架事务管理

Spring声明式事务

声明式事务是Spring框架提供的一种通过配置而非编程代码来管理事务的方式。

相关的类和API

  1. PlatformTransactionManager接口:平台事务管理器

    • 该接口根据不同的持久层框架,选择不同的具体实现类:
    • 接口方法:
      • void commit(TransactionStatus status)
      • void rollback(TransactionStatus status)
      • 使用Spring框架的JDBC模板或者MyBatis框架,选择DataSourceTransactionManager实现类
      • 使用Hibernate框架,选择HibernateTransactionManager实现类
  2. TransactionDefinition接口,事务定义信息接口

    • 定义了事务隔离级别
    • 定义了事务传播行为

Spring框架声明式事务管理

声明式事务管理

有三种形式:

  1. 配置文件形式
  2. 半注解形式(配置文件 + 注解)
  3. 全注解形式(注解)

配置文件形式

Bean管理的元素属性

  1. <tx:advice>:事务通知配置
    • id:事务通知标识符,用于其它地方引用
    • transaction-manager:指定事务管理器Bean的id叫什么
    • <tx:attributes>:定义方法级别的事务属性
    • <tx:method>:为匹配的方法指定详细的事务行为规则
    • name:匹配XX的方法
    • isolation:隔离级别
    • read-only
  2. <aop:config>:AOP通知配置
    • <aop:advisor>:系统通知
    • advice-ref:引用上面的事务
    • pointcut:定义切入点表达式

<aop:advisor><aop:aspect> 的区别:

特性<aop:advisor><aop:aspect>
用途系统级通知自定义业务通知
通知类型通常使用Around通知支持5种通知
配置方式引用外部定义的Advice在aspect内部定义通知方法
灵活性相对固定更灵活,可自定义逻辑

实操案例

  1. 创建maven Java项目
  2. 引入依赖
    <!-- Maven中配置Java版本 -->
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <!--有单元测试的环境,Spring5版本,Junit4.12版本-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- 连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!-- mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!-- Spring整合Junit测试的jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <!-- AOP联盟 -->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <!-- Spring Aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <!-- aspectj -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
    </dependencies>
    
  3. 创建实体类:Account类
    • 添加id、name、money这三个成员变量(字段)
    • 获取它们的getter/setter方法
  4. 创建持久层的接口类和实现类:AccountDao类和AccountDaoImpl类
    package com.qcby.dao;
    
    public interface AccountDao {
        /**
         * 转账
         * @param name  转账人
         * @param money 金额
         */
        public void outMoney(String name, Double money);
    
        /**
         * 收账
         * @param name  收账人
         * @param money 金额
         */
        public void inMoney(String name, Double money);
    }
    
    package com.qcby.dao.impl;
    
    import com.qcby.dao.AccountDao;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    public class AccountDaoImpl implements AccountDao {
        private JdbcTemplate jdbcTemplate;
    
        public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
    
        @Override
        public void outMoney(String name, Double money) {
            //编写sql语句
            String sql = "update account set money = money - ? where name = ?";
            jdbcTemplate.update(sql, money, name);
        }
    
        @Override
        public void inMoney(String name, Double money) {
            String sql = "update account set money = money + ? where name = ?";
            jdbcTemplate.update(sql, money, name);
        }
    }
    
  5. 创建业务层的接口来和实现类:AccountService类和AccountServiceImpl类
    package com.qcby.service;
    
    public interface AccountService {
        /**
         *
         * @param out   转账
         * @param in    收账
         * @param money 金额
         */
        public void pay(String out, String in, Double money);
    }
    
    package com.qcby.service.impl;
    
    import com.qcby.dao.AccountDao;
    import com.qcby.service.AccountService;
    
    public class AccountServiceImpl implements AccountService {
    
        private AccountDao accountDao;
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
        /**
         *
         * @param out   转账
         * @param in    收账
         * @param money 金额
         */
        @Override
        public void pay(String out, String in, Double money) {
            accountDao.outMoney(out,money);
            accountDao.inMoney(in,money);
        }
    }
    
  6. 编写属性文件:jdbc.properties
    jdbc.driverClassName=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql:///spring_db
    jdbc.username=username // 你的用户名
    jdbc.password=userpassword // 你的密码
    
  7. 编写配置文件:applicationContext.xml
    • 加载属性文件,并配置参数
    • 配置平台事务管理器
    • 配置事务通知(有Spring提供的切面类)
    • 配置AOP增强
    • 配置jdbc模板
    • 配置Service
    • 配置dao
    <?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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           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
                               http://www.springframework.org/schema/aop
                               http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--配置文件形式-->
        <!--使用标签方式-->
        <!--加载属性文件-->
        <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <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>
    
        <!--配置平台事务管理器-->
        <bean id="tran" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!--配置事务的通知(由Spring提供的切面类)-->
        <tx:advice id="txAdvice" transaction-manager="tran">
            <tx:attributes>
                <!--对pay进行增强,设置隔离级别,传播行为,超时时间-->
                <tx:method name="pay" isolation="DEFAULT"/>
                <tx:method name="find*" read-only="true"></tx:method>
            </tx:attributes>
        </tx:advice>
    
        <!--配置AOP增强-->
        <aop:config>
            <!--Spring框架提供系统通知,使用advisor标签-->
            <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.qcby.service.impl.AccountServiceImpl.pay(..))"></aop:advisor>
        </aop:config>
    
        <!--配置jdbc模板-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!--配置Service-->
        <bean id="accountService" class="com.qcby.service.impl.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"></property>
        </bean>
    
        <!--配置Dao-->
        <bean id="accountDao" class="com.qcby.dao.impl.AccountDaoImpl">
            <property name="jdbcTemplate" ref="jdbcTemplate"></property>
        </bean>
    </beans>
    
  8. 创建测试类:AccountTest类
    package com.qcby.text;
    
    import com.qcby.service.AccountService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * IOC:配置文件形式
     * JDBC模板,配置文件形式 + 属性文件
     * 声明式事务:配置文件形式(Spring框架提供AOP)
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class AccountTest {
        @Autowired
        public AccountService accountService;
    
        @Test
        public void run(){
            accountService.pay("美羊羊","沸羊羊",100.0);
        }
    }
    

半注解形式(配置文件 + 注解)

常用的注解

  • @Transactional()注解:将一个方法或类标记为需要事务性执行
    • 也就是说,在方法上添加了这个注解,Spring会在这个方法上开始执行前自动开启一个数据库事务,方法完毕后提交事务,出现异常回滚事务。
  • isolation属性:设置事务隔离级别
  • @EnableAspectJAutoProxy注解:开启事务注解
  • @PropertySource("classpath:jdbc.properties")注释:读取属性文件

实操案例

  1. 前三步与上面配置文件形式一样
  2. 持久层种的实现类,加上注解
    @Repository("accountDao2")
    public class AccountDaoImpl2 implements AccountDao {
        @Autowired
        private JdbcTemplate jdbcTemplate;
    		// ……其它代码与上面一致
    }
    
  3. 业务层种的实现类,加上注解
    @Service("accountService2")
    // 设置事务级别
    @Transactional(isolation = Isolation.DEFAULT)
    public class AccountServiceImpl2 implements AccountService{
        @Autowired
        @Qualifier("accountDao2")
        private AccountDao accountDao;
    		// ……其它代码与上面一致
    }
    
  4. 属性文件内容不变
  5. 配置文件内容有修改:applicationContext2.xml
    • 去掉配置Service、配置Dao、配置AOP增强的Bean管理代码,修改为开启注解扫描
    • 去掉配置事务的通知(由Spring提供的切面类)的相关代码,修改为开启事务注解支持
    <?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.qcby">
            <!-- 排除标注了@Configuration的类 -->
            <context:exclude-filter type="annotation"
                                    expression="org.springframework.context.annotation.Configuration"/>
        </context:component-scan>
    
        <!--标签方式-->
        <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
        <!--加载属性文件-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <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>
    
        <!--配置平台事务管理-->
        <bean id="tean" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!--配置JDBC模板-->
        <bean id="jdebTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!--开启事务注解支持-->
        <tx:annotation-driven transaction-manager="tean"></tx:annotation-driven>
    </beans>
    
  6. 创建测试类:AccountTest2类
    package com.qcby.text;
    
    import com.qcby.service.AccountService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * IOC:配置文件形式 + 注解
     * JDBC模板,配置文件形式 + 属性文件
     * 声明式事务:配置文件形式 + 注解
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext2.xml")
    public class AccountTest2 {
        @Autowired
        @Qualifier("accountService2")
        public AccountService accountService;
    
        @Test
        public void run(){
            accountService.pay("美羊羊","沸羊羊",100.0);
        }
    }
    

全注解形式

实操案例

  1. 前3步与上面半注解形式一致
  2. 不在需要属性文件和配置文件,只需要创建配置类:SpringConfig类
    • 配置数据库连接池
    • 创建模板对象
    • 创建平台事务管理对象
    package com.qcby.tools;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    import org.springframework.transaction.PlatformTransactionManager;
    
    import javax.annotation.Resource;
    import javax.sql.DataSource;
    
    @Configuration
    @ComponentScan("com.qcby")
    // 开启事务注解
    @EnableAspectJAutoProxy
    public class SpringConfig {
    
        /**
         * 连接数据库
         * @return
         * @throws Exception
         */
        @Bean("dataSource")
        public DataSource dataSource() throws Exception{
            // 创建连接池对象,Spring框架内置了连接池对象
            DriverManagerDataSource ds = new DriverManagerDataSource();
            // 设置4个参数
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql:///spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    
        /**
         * 创建模板对象
         * @param dataSource
         * @return
         */
        @Resource(name = "dataSource") // 不仅可以作用在属性上,也可以作用方法上
        @Bean("jdbcTemplate") // 把JdbcTemplate保存到IOC容器中
        public JdbcTemplate createJdbcTemplate(DataSource dataSource){
            JdbcTemplate template = new JdbcTemplate(dataSource);
            return template;
        }
    
        /**
         * 创建平台事务管理器对象
         * @param dataSource
         * @return
         */
        @Resource(name = "dataSource")
        @Bean("manager")
        public PlatformTransactionManager createTransactionManager(DataSource dataSource){
            DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
            return manager;
        }
    }
    
  3. 创建测试类:AccountTest3类
    package com.qcby.text;
    
    import com.qcby.service.AccountService;
    import com.qcby.tools.SpringConfig;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * IOC全注解:注解类
     * JDBC模板全注解,注解类
     * 声明式事务:注解类
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {SpringConfig.class})
    public class AccountTest3 {
        @Autowired
        @Qualifier("accountService3")
        public AccountService accountService;
    
        @Test
        public void run(){
            accountService.pay("美羊羊","沸羊羊",100.0);
        }
    }
    
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值