spring控制事务:声明式事务(注解)

声明式事务(注解)

spring声明事务的方式,使用注解的方式

@Transactional

⚫ 名称:@Transactional
⚫ 类型:方法注解,类注解,接口注解
⚫ 位置:方法定义上方,类定义上方,接口定义上方
⚫ 作用:设置当前类/接口中所有方法或具体方法开启事务,并指定相关事务属性
⚫ 范例:

@Transactional(
readOnly = false,
timeout = -1,
isolation = Isolation.DEFAULT,
rollbackFor = {ArithmeticException.class, IOException.class},
noRollbackFor = {},
propagation = Propagation.REQUIRES_NEW
)

tx:annotation-driven 标签用于开启事务的注解驱动

⚫ 名称:tx:annotation-driven
⚫ 类型:标签
⚫ 归属:beans标签
⚫ 作用:开启事务注解驱动,并指定对应的事务管理器
⚫ 范例:

<tx:annotation-driven transaction-manager="txManager"/>

声明式事务(纯注解驱动)

@EnableTransactionManagement 这个注解就是代替上面的标签

⚫ 名称:@EnableTransactionManagement
⚫ 类型:类注解
⚫ 位置:Spring注解配置类上方
⚫ 作用:开启注解驱动,等同XML格式中的注解驱动
⚫ 范例:

@Configuration
@ComponentScan("com.fs")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class,TransactionManagerConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
public class TransactionManagerConfig {
@Bean
public PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}

代码演示(代码中注释为详细解释)

数据表account

在这里插入图片描述

构建maven项目

在这里插入图片描述

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.fs</groupId>
    <artifactId>day04_spring_AOP_Transaction_02</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>

        <!--        jdbc-->

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>

        <!--        spring整合mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>
        <!--        mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.20</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <!--        aop切面包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.93.132:3306/test
jdbc.username=root
jdbc.password=root

编写配置类

SpringConfig
package com.fs.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;

//表示这个类是一个spring的配置类,并将其存入到ioc容器中
@Configuration
/*开启扫描注解的那些包
<!--    启动spring扫描注解的包,只扫描spring的注解-->
    <context:component-scan base-package="com.fs"/>
 */
@ComponentScan("com.fs")
//引入其他的配置类
@Import({JdbcConfig.class,MybatisConfig.class,TransactionManagerConfig.class})
//开启切面自动代理
@EnableAspectJAutoProxy
//开启声明式事务管理    相当于配置文件中的 <tx:annotation-driven transaction-manager="transactionManager"/>
@EnableTransactionManagement
public class SpringConfig {
}

JdbcConfig
package com.fs.config;


import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

/*
<!--    引入properties-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
 */
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
    //@Value("${Key}")
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;


    /*
    <!--    整合druid,把DruidDataSource交给spring管理-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
     */
    //将返回的DruidDataSource交给ioc管理
    @Bean
    public DruidDataSource getDruidDataSource(){
        //创建DruidDataSource
        DruidDataSource druidDataSource = new DruidDataSource();
        //给属性赋值,这个是上面从配置文件中依耐注入给属性的
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        //返回DruidDataSource,然后交给ioc管理
        return druidDataSource;
    }
}

MybatisConfig
package com.fs.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MybatisConfig {


    /*
        <!--    配置MyBatis的会话工厂类  mybatis.spring 下的SqlSessionFactoryBean
    配置的sqlSessionFactory得到SqlSession,然后MyBatis从spring中拿到SqlSession.getMapper()去动态代理dao-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--        给MyBatis配置链接池,依耐注入-->
        <property name="dataSource" ref="dataSource"/>
<!--        配置别名扫描的包,被扫描的包下的类起的别名就是类名首字母小写,用于mapper.xml文件中使用-->
        <property name="typeAliasesPackage" value="com.fs.pojo"/>
    </bean>
     */
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){
        //创建sql会话工厂类                                //这里@Autowired也是从ioc拿DataSource
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //从ioc容器中取出jdbcConfig中配置的druidDatasource
        sqlSessionFactoryBean.setDataSource(dataSource);
        //配置起别名
        sqlSessionFactoryBean.setTypeAliasesPackage("com.fs.pojo");
        return sqlSessionFactoryBean;
    }

    /*
    <!--配置MyBatis扫描dao的包,让MyBatis动态代理生成这个dao的实现类,并交给ioc管理
        mybatis-spring这个包下MapperScannerConfigurer提供了spring于MyBatis的整合-->
    <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--        告诉MyBatis我dao在哪里.然后MyBatis将这个dao实现,然后给spring管理-->
        <property name="basePackage" value="com.fs.dao"/>
    </bean>
     */
    //创建MyBatis动态代理扫描类
    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(){
        //创建映扫描配置类
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        //设置动态代理扫描的dao的包
        mapperScannerConfigurer.setBasePackage("com.fs.dao");
        //交给spring管理
        return mapperScannerConfigurer;
    }
}

TransactionManagerConfig
package com.fs.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/*
声明式事务配置类
 */
public class TransactionManagerConfig {

    /*
    <!--    将事务管理器交给ioc管理-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
     */
    @Bean
    public PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){
        //使用Spring的平台事务管理器,是一个接口,使用他的实现类构造器传递一个连接池
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        //传递一个连接池
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

实体类

Account
package com.fs.pojo;

import lombok.Data;

@Data
public class Account {
    private Integer id;
    private String name;
    private Double money;
}

Dao

AccountDao
package com.fs.dao;

import com.fs.pojo.Account;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface AccountDao {
    @Select("select * from account")
    List<Account> findAll();//查询所有

    //根据名字修改money
    @Update("UPDATE account SET money = #{money} WHERE name = #{name}")
    void transferMoney(Account account);

    //根据名字查询账户信息
    @Select("select * from account where name = #{name}")
    Account findAccountByName(@Param("name") String name);
}

service

AccountService
package com.fs.service;

import com.fs.pojo.Account;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/*
在接口上配置@Transactional,那么他下面的实现类都会进行事务管理
这个注解就相当于配置文件中的这些配置
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.fs.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
    </aop:config>

如果不写属性,该注解的默认配置为
    @Transactional(
        readOnly = false,
        timeout = -1,
        isolation = Isolation.DEFAULT,
        rollbackFor = {ArithmeticException.class, IOException.class},
        noRollbackFor = {},
        propagation = Propagation.REQUIRES_NEW
    )
 */
public interface AccountService {
    List<Account> findAll();

    Account findAccountByName(String name);

    //对这个方法开启事务使用默认配置
    @Transactional
    void transferMoneyAtoB(String aName,String bName,Integer money);
}

AccountServiceImpl
package com.fs.service.impl;

import com.fs.dao.AccountDao;
import com.fs.pojo.Account;
import com.fs.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

/*
    <!--  把业务类 AccountServiceImpl 交给ioc管理 -->
    <bean id="accountServiceImpl" class="com.fs.service.impl.AccountServiceImpl">
<!--       依耐注入dao,这个dao被MyBatis动态代理实现后被spring存放在ioc容器中-->
        <property name="accountDao" ref="accountDao"/>
    </bean>
 */
@Service
public class AccountServiceImpl implements AccountService {

    //从ioc获取MyBatis动态代理的accountDao实现类
    @Autowired
    private AccountDao accountDao;

    @Autowired
    private DataSource dataSource;

    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }

    @Override
    public Account findAccountByName(String name) {
        return accountDao.findAccountByName(name);
    }

    //转账的业务实现
    @Override
    public void transferMoneyAtoB(String aName, String bName, Integer money) {
        //先查出两个人的数据
        Account aAccount = accountDao.findAccountByName(aName);
        Account bAccount = accountDao.findAccountByName(bName);

        //然后a减钱,b加钱
        aAccount.setMoney(aAccount.getMoney()-money);
        bAccount.setMoney(bAccount.getMoney()+money);


        //然后调用转账方法(sql语句为更新账户)
        accountDao.transferMoney(aAccount);
        //制作一个异常
//        int i = 1/0;
        accountDao.transferMoney(bAccount);
    }

}

测试类

package com.fs.service.impl;

import com.fs.config.SpringConfig;
import com.fs.pojo.Account;
import com.fs.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;

import java.util.List;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceImplTest {

    @Autowired
    private AccountService accountService;

    //测试查询所有方法
    @Test
    public void findAll() {
        List<Account> all = accountService.findAll();
        System.out.println(all);
    }

    //测试转账方法已经使用@Transactional绑定事务
    @Test
    public void transferMoneyAtoB() {
        //测试转账方法
        accountService.transferMoneyAtoB("小付","小花",100);
    }

}

执行效果

业务层转账方法正常运行成功后数据表数据

由于我们测试代码中是小付向小花转账100元,代码执行成功后应该小付900,小花1100
在这里插入图片描述
业务层转账方法我们给制作一个异常1/0 的/ by zero异常,
运存测试转账方法控制台输出
在这里插入图片描述
数据库中表的数据
小付原本900,小花1100,我们进行了转账业务,但是制作了异常,事务会进行回滚,所以金额不会发生变化

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值