SSM框架-AOP概述、Spring事务
创始人
2024-05-26 22:02:12
0

16 spring整合mybatis

16.1 前情代码

  1. 实体类
public class Account {private Integer id;private String name;private Double money;public Integer getId() {return id;}public void setId(Integer 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;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}
  1. AccountDao接口
public interface AccountDao {@Insert("insert into tbl_account(name,money)values(#{name},#{money})")void save(Account account);@Delete("delete from tbl_account where id = #{id} ")void delete(Integer id);@Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")void update(Account account);@Select("select * from tbl_account")List findAll();@Select("select * from tbl_account where id = #{id} ")Account findById(Integer id);
}
  1. App实现
public class App {public static void main(String[] args) throws IOException {// 1. 创建SqlSessionFactoryBuilder对象SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();// 2. 加载SqlMapConfig.xml配置文件InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");// 3. 创建SqlSessionFactory对象SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);// 4. 获取SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();// 5. 执行SqlSession对象执行查询,获取结果UserAccountDao accountDao = sqlSession.getMapper(AccountDao.class);Account ac = accountDao.findById(2);System.out.println(ac);// 6. 释放资源sqlSession.close();}
}
  1. AccountService接口
public interface AccountService {void save(Account account);void delete(Integer id);void update(Account account);List findAll();Account findById(Integer id);}
  1. AccountServiceImpl接口实现类
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public void save(Account account) {accountDao.save(account);}public void update(Account account){accountDao.update(account);}public void delete(Integer id) {accountDao.delete(id);}public Account findById(Integer id) {return accountDao.findById(id);}public List findAll() {return accountDao.findAll();}
}

16.2 整合

1. 分析

在这里插入图片描述

sqlSession是工厂造出来的,已经存在,现在调用而已

sqkSessionFactory是核心对象

下面配置是围绕此对象进行的
在这里插入图片描述

  1. pom准备
 org.springframeworkspring-context5.2.10.RELEASEcom.alibabadruid1.1.16org.mybatismybatis3.5.6mysqlmysql-connector-java5.1.47org.springframeworkspring-jdbc5.2.10.RELEASEorg.mybatismybatis-spring1.3.0org.mybatismybatis-spring1.3.0
  1. spring配置类
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {}
  1. 自动状态JdbcConfig配置类
public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}
}
  1. 注解简化mybatis配置

在这里插入图片描述

public class MybatisConfig {//定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象@Beanpublic SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.itheima.domain");ssfb.setDataSource(dataSource);return ssfb;}//定义bean,返回MapperScannerConfigurer对象@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.itheima.dao");return msc;}
}
  1. 原来App类不要了,重新写App2
public class App2 {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);AccountService accountService = ctx.getBean(AccountService.class);Account ac = accountService.findById(1);System.out.println(ac);}
}

17 Spring整合junit

  1. maven准备
    junitjunit4.12org.springframeworkspring-test5.2.10.RELEASE
  1. 写测试方法

junit就相当于不用走main方法直接执行

//设定类运行器
@RunWith(SpringJUnit4ClassRunner.class)
//配置上下文
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {//测业务层接口//自动装配@Autowiredprivate AccountService accountService;//写测试方法@Testpublic void testFindById(){//直接掉方法就可以啦System.out.println(accountService.findById(2));}}

18 AOP

18.1 AOP简介

  1. 简介

AOP面向切面编程,一种编程规范,指导开发者如何组织程序结构

  1. 作用

在不惊动原始设计的基础上为其进行功能增强

  1. 原理

将原来想要其他方法也拥有的功能代码抽取出来,把这块东西抽取出来成method单独的方法

给原来就有的方法取名叫做连接点

给需要追加功能的方法叫做切入点

追加的共性功能的方法(method)中叫做通知

通知方法所在的类叫做通知类

通知和切入点关系叫做切面

在这里插入图片描述

在这里插入图片描述

18.2 AOP入门案例

  1. 步骤分析
  • 导入AOP坐标(pom.xml)
  • 制作连接点方法(原始操作,Dao接口与实现类)
  • 制作共性功能(通知类与通知)
  • 定义切入点
  • 绑定切入点和通知关系(切面)
  1. pom包导入依赖

导入context的时候,aop的包也自动导入了

导入aspectj的包

    org.springframeworkspring-context5.2.10.RELEASEorg.aspectjaspectjweaver1.9.4
  1. BookDaoImpl,制作连接点方法
  • 接口
public interface BookDao {public void save();public void update();
}
  • 实现类
@Repository
public class BookDaoImpl implements BookDao {public void save() {System.out.println(System.currentTimeMillis());System.out.println("book dao save ...");}public void update(){System.out.println("book dao update ...");}
}
  1. 制作连接点方法以及定义切入点
  • 新建一个类advice写这个
//4.通知类必须配置成Spring管理的bean
@Component
//5.设置当前类为切面类类,spring就会把这个当作aop处理
@Aspect
public class MyAdvice {//2.定义切入点,要求配置在方法上方@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}//3.设置在切入点pt()的前面运行当前操作(前置通知)@Before("pt()")//1.共性功能public void method(){System.out.println(System.currentTimeMillis());}
}
  • spring配置类也要写一个
@Configuration
@ComponentScan("com.itheima")
//开启注解开发AOP功能,跟@Aspect一起的
@EnableAspectJAutoProxy
public class SpringConfig {
}
  • App入口
public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = ctx.getBean(BookDao.class);bookDao.update();}
}
  • 输出结果
1668245211570
book dao save ...

18.3 AOP的工作流程

  1. 流程
  • Spring容器启动
  • 读取所有切面配置中的切入点
  • 初始化bean,判断bean对应的类中的方法是否匹配到任意切入点
    • 匹配失败,创建对象
    • 匹配成功,创建原始对象(目标对象)的代理对象
  • 执行bean执行方法
    • 获取bean,调用方法并执行,完成操作
    • 获取bean是代理对象时,根据代理对象的运行模式运行原始方法和增强方法的内容,完成操作
  1. 实操
  • 匹配的上

System.out.println(bookDao);打印出来是System.out.println(bookDao);

System.out.println(bookDao.getClass());打印出来是class com.sun.proxy.$Proxy19

  • 匹配不上

System.out.println(bookDao);打印出来是System.out.println(bookDao);

System.out.println(bookDao.getClass());打印出来是class com.itheima.dao.impl.BookDaoImpl

  • 总结

打印对象不准确,因为对tostring进行重写了,所以看到的是一个样的

打印.class发现是由代理完成的实现的

18.4 AOP切入点表达式

image-20221112210448849
  1. 语法格式
  • 类型

    • 按照接口描述

    • 按照接口实现类描述,但一般不用,耦合度高

  • 切入点表达式标准格式

在这里插入图片描述

  • 实例
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")@Pointcut("execution(void com.itheima.dao.impl.BookDaoImpl.update())")
  1. 通配符
  • *:表示任意
    @Pointcut("execution(* com.itheima.dao.impl.BookDaoImpl.update())")//出@Pointcut("execution(* com.itheima.dao.impl.BookDaoImpl.update(*))")//不出,因此*表示至少一个
  • …:表示多个连续的任意符号,即没有或者多个
    @Pointcut("execution(void *..update())")@Pointcut("execution(* *..*(..))")//表示匹配工程中的所有东西,电脑估计会炸,一般不用@Pointcut("execution(* *..u*(..))")//u开头的方法
  • +:专用于匹配子类类型
  1. 技巧与规范

在这里插入图片描述

18.5 AOP通知类型

  1. 概念

描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置

  1. 类型
  • 前置通知
  • 后置通知
  • 环绕通知(重点)
  • 返回后通知(了解)
  • 抛出异常后通知(了解)
  1. 运用
  • 前置
public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}//@Before:前置通知,在原始方法运行之前执行@Before("pt()")public void before() {System.out.println("before advice ...");}
}
  • 后置
    //@After:后置通知,在原始方法运行之后执行@After("pt()")public void after() {System.out.println("after advice ...");}
  • 环绕通知

需要加上对原始操作的调用

    //@Around:环绕通知,在原始方法运行的前后执行@Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ...");//表示对原始操作的调用Object ret = pjp.proceed();System.out.println("around after advice ...");return ret;}

如果接口有返回值,@Around需要返回Object,通过 pjp.proceed()进行返回值的接收

public interface BookDao {public int select();
}
public class BookDaoImpl implements BookDao {public int select() {System.out.println("book dao select is running ...");
//        int i = 1/0;return 100;}
}
    @Around("pt2()")public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ...");//表示对原始操作的调用Integer ret = (Integer) pjp.proceed();System.out.println("around after advice ...");return ret;}

在这里插入图片描述

  • 返回后通知(了解)

与After的区别是要求原始方法执行过程中未出现异常现象

    //@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象@AfterReturning("pt2()")public void afterReturning() {System.out.println("afterReturning advice ...");}
  • 抛出异常后通知
    //@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行@AfterThrowing("pt2()")public void afterThrowing() {System.out.println("afterThrowing advice ...");}

18.6 测试业务层接口万次执行效率

  1. 需求

任意业务层接口执行均可显示其执行效率(执行时长)

  1. 分析
  • 业务功能:业务层接口执行前后分别记录时间,求差值得到执行效率
  • 通知类型选择前后均可以增强的类型—环绕通知
  1. 前情代码
  • jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=123456
  • 配置类JdbcConfig
package com.itheima.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.Configuration;import javax.sql.DataSource;public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}
}
  • 配置类MybatisConfig
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;import javax.sql.DataSource;public class MybatisConfig {@Beanpublic SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.itheima.domain");ssfb.setDataSource(dataSource);return ssfb;}@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.itheima.dao");return msc;}
}
  • spring配置类SpringConfig
import org.springframework.context.annotation.*;@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
  • 实体类Account
package com.itheima.domain;import java.io.Serializable;public class Account implements Serializable {private Integer id;private String name;private Double money;public Integer getId() {return id;}public void setId(Integer 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;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}
  • AccountDao接口(使用mybatis)
public interface AccountDao {@Insert("insert into tbl_account(name,money)values(#{name},#{money})")void save(Account account);@Delete("delete from tbl_account where id = #{id} ")void delete(Integer id);@Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")void update(Account account);@Select("select * from tbl_account")List findAll();@Select("select * from tbl_account where id = #{id} ")Account findById(Integer id);
}
  • AccountService接口
package com.itheima.service;import com.itheima.domain.Account;import java.util.List;public interface AccountService {void save(Account account);void delete(Integer id);void update(Account account);List findAll();Account findById(Integer id);
}
  • AccountServiceImpl接口实现类
import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Overridepublic void save(Account account) {accountDao.save(account);}@Overridepublic void delete(Integer id) {accountDao.delete(id);}@Overridepublic void update(Account account) {accountDao.update(account);}@Overridepublic List findAll() {return accountDao.findAll() ;}@Overridepublic Account findById(Integer id) {return accountDao.findById(id);}
}
  • 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTestCase {@Autowiredprivate AccountService accountService;@Testpublic void testFindById(){Account ac = accountService.findById(2);System.out.println(ac);}@Testpublic void testFindAll(){List all = accountService.findAll();System.out.println(all);}
}
  1. 进行aop设置
  • 打开aop注解
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableAspectJAutoProxy
public class SpringConfig {
}
  • 写aop的类
@Component
//切片类
@Aspect
public class ProjectAdvice {//匹配业务层的所有方法@Pointcut("execution(* com.itheima.service.*Service.*(..))")private void servicePt(){};@Around("ProjectAdvice.servicePt()")public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {//签名信息Signature signature = pjp.getSignature();//接口类型String className = signature.getDeclaringTypeName();//接口方法String methodName = signature.getName();//原始操作前来个记录时间long start = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {//执行原始操作,不需要返回值了,直接查询10000遍pjp.proceed();}//结束后long end = System.currentTimeMillis();System.out.println("万次执行:"+className+"."+methodName+"---->"+(end-start)+"ms");}
}
  • 测试代码
@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Overridepublic void save(Account account) {accountDao.save(account);}@Overridepublic void delete(Integer id) {accountDao.delete(id);}@Overridepublic void update(Account account) {accountDao.update(account);}@Overridepublic List findAll() {return accountDao.findAll() ;}@Overridepublic Account findById(Integer id) {return accountDao.findById(id);}
}
  • 结果
万次执行:com.itheima.service.AccountService.findAll---->2148ms
万次执行:com.itheima.service.AccountService.findById---->1280ms

18.7 AOP通知获取数据

在这里插入图片描述

  1. 代码
  • before
@Component
@Aspect
public class MyAdvice {@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")private void pt(){}//JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数@Before("pt()")public void before(JoinPoint jp) {//用JoinPoint返回参数,返回的是数组Object[] args = jp.getArgs();System.out.println(Arrays.toString(args));System.out.println("before advice ..." );}}
  • around

正常运行是id为100,可以在中间更改参数,变成600

可以运用到参过来的参数有问题(例如数据格式、数据问题),可以处理

    //ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用,直接getArgs//@Around("pt()")public Object around(ProceedingJoinPoint pjp) {Object[] args = pjp.getArgs();System.out.println(Arrays.toString(args));//更改args数值,再作为参数扔进去args[0] = 666;Object ret = null;try {ret = pjp.proceed(args);} catch (Throwable t) {t.printStackTrace();}return ret;}
  • afterReturning

returning = "ret"与String ret以及JoinPoint和String同时存在

    //设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同@AfterReturning(value = "pt()",returning = "ret")//JoinPoint和String同时存在,JoinPoint得放前面public void afterReturning(JoinPoint jp,String ret) {System.out.println("afterReturning advice ..."+ret);}
  • AfterThrowing

throwing = "t"与(Throwable t)

    //设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同,即形参是返回对象@AfterThrowing(value = "pt()",throwing = "t")public void afterThrowing(Throwable t) {System.out.println("afterThrowing advice ..."+t);}
    public String findName(int id,String password) {System.out.println("id:" + id);//故意来个异常,给AfterThrowing接收if (true) throw new NullPointerException();return "itcast";}

输出

id:100
afterThrowing advice ...java.lang.NullPointerException
Exception in thread "main" java.lang.NullPointerException
  1. 总结
  • around用proceedingjoinpoint,其他用joinpoint

18.8 案例:百度网盘密码数据兼容处理

  1. 需求

在业务方法执行之前对所有的输入参数进行格式处理——trim()

  1. 分析

使用处理后的参数调用原始方法——环绕通知中存在对原始方法的调用

  1. 前情代码
  • maven
 org.springframeworkspring-context5.2.10.RELEASEorg.springframeworkspring-jdbc5.2.10.RELEASEorg.springframeworkspring-test5.2.10.RELEASEorg.aspectjaspectjweaver1.9.4mysqlmysql-connector-java5.1.47com.alibabadruid1.1.16org.mybatismybatis3.5.6org.mybatismybatis-spring1.3.0junitjunit4.12test
  • 配置类
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}
  • ResourcesDao接口
public interface ResourcesDao {boolean readResources(String url, String password);
}
  • ResourcesDaoImpl接口实现类
@Repository
public class ResourcesDaoImpl implements ResourcesDao {public boolean readResources(String url, String password) {System.out.println(password.length());//模拟校验return password.equals("root");}
}
  • ResourcesService
public interface ResourcesService {public boolean openURL(String url, String password);
}
  • ResourcesServiceImpl接口实现类
@Service
public class ResourcesServiceImpl implements ResourcesService {@Autowiredprivate ResourcesDao resourcesDao;public boolean openURL(String url, String password) {return resourcesDao.readResources(url,password);}
}
  • App入口
public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);ResourcesService resourcesService = ctx.getBean(ResourcesService.class);boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root");System.out.println(flag);}
}
  1. 开工
  • 打开aop配置类
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}
  • DataAdvice类
@Component
@Aspect
public class DataAdvice {@Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))")private void servicePt(){}@Around("com.itheima.aop.DataAdvice.servicePt()")public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {Object[] args = pjp.getArgs();for (int i = 0; i < args.length; i++) {//args是个对象,不一定是字符产,不用foreach循环,用fori,需要用到索引值判断是否是字符串// 判断参数是不是字符串if(args[i].getClass().equals(String.class)){//用toString转成字符串args[i] = args[i].toString().trim();}}//改完放回去哦Object ret = pjp.proceed(args);return ret;}
}
  • 运行App入口类
public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);ResourcesService resourcesService = ctx.getBean(ResourcesService.class);boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root  ");System.out.println(flag);}
}
  • 输出结果
4
true

19 Spring事务

19.1 spring事务简介

  1. 作用

在数据层保障一系列的数据库操作同失败同完成

spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败

在这里插入图片描述

  1. 需求

需求:实现任意两个账户间转账操作,A账户减钱,B账户加钱

  • JDBC配置
public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}
}
  • mybatis配置
public class MybatisConfig {@Beanpublic SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.itheima.domain");ssfb.setDataSource(dataSource);return ssfb;}@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.itheima.dao");return msc;}
}
  • spring配置
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
  • 数据层接口
public interface AccountDao {@Update("update tbl_account set money = money + #{money} where name = #{name}")void inMoney(@Param("name") String name, @Param("money") Double money);@Update("update tbl_account set money = money - #{money} where name = #{name}")void outMoney(@Param("name") String name, @Param("money") Double money);
}
  • 业务层接口
public interface AccountService {/*** 转账操作* @param out 传出方* @param in 转入方* @param money 金额*///配置当前接口方法具有事务@Transactionalpublic void transfer(String out,String in ,Double money) ;
}
  • 业务层实现类
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public void transfer(String out,String in ,Double money) {accountDao.outMoney(out,money);accountDao.inMoney(in,money);}
}
  • 测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Testpublic void testTransfer() throws IOException {accountService.transfer("Tom","Jerry",100D);}
}
  1. 结果分析

如果中间出现异常,那么会出现A加,B不减的执行问题

@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public void transfer(String out,String in ,Double money) {accountDao.outMoney(out,money);int i = 1/0;accountDao.inMoney(in,money);}}

因此在接口中开启事务

    @Transactionalpublic void transfer(String out,String in ,Double money) ;
}

并且在JdbcConfig开启事务管理器

    //配置事务管理器,mybatis使用的是jdbc事务,dataSource这个丢进去@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource){DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(dataSource);return transactionManager;}

并且在注解写上事务型驱动

@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
//开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {
}

这样运行的时候就会保证同时成功或者失败,抛出异常java.lang.ArithmeticException: / by zero

19.2 Spring事务角色

  1. 场景

在这里插入图片描述

outMoney事务和inMoney事务都会加入到transfer事务中,就可以实现,transfer下的操作同时成功或者失败,不会出现inMoney事务出错,而outMoney事务不回滚的问题了

transfer叫做事务管理员,outMoney事务和inMoney事务叫做事务协调员

事务管理员就是发起事务方,就是发起事务的那个方法,例如transfer

事务协调员就是加入事务方,可以是数据层方法也可以是业务层方法

19.3 事务的配置

  1. 配置项

在这里插入图片描述

相关内容

热门资讯

Python|位运算|数组|动... 目录 1、只出现一次的数字(位运算,数组) 示例 选项代...
张岱的人物生平 张岱的人物生平张岱(414年-484年),字景山,吴郡吴县(今江苏苏州)人。南朝齐大臣。祖父张敞,东...
西游西后传演员女人物 西游西后传演员女人物西游西后传演员女人物 孙悟空 六小龄童 唐僧 徐少华 ...
名人故事中贾岛作诗内容简介 名人故事中贾岛作诗内容简介有一次,贾岛骑驴闯了官道.他正琢磨着一句诗,名叫《题李凝幽居》全诗如下:闲...
和男朋友一起优秀的文案? 和男朋友一起优秀的文案?1.希望是惟一所有的人都共同享有的好处;一无所有的人,仍拥有希望。2.生活,...
戴玉手镯的好处 戴玉手镯好还是... 戴玉手镯的好处 戴玉手镯好还是碧玺好 女人戴玉?戴玉好还是碧玺好点佩戴手镯,以和田玉手镯为佳!相嫌滑...
依然什么意思? 依然什么意思?依然(汉语词语)依然,汉语词汇。拼音:yī    rán基本解释:副词,指照往常、依旧...
高尔基的散文诗 高尔基的散文诗《海燕》、《大学》、《母亲》、《童年》这些都是比较出名的一些代表作。
心在飞扬作者简介 心在飞扬作者简介心在飞扬作者简介如下。根据相关公开资料查询,心在飞扬是一位优秀的小说作者,他的小说作...
卡什坦卡的故事赏析? 卡什坦卡的故事赏析?讲了一只小狗的故事, 我也是近来才读到这篇小说. 作家对动物的拟人描写真是惟妙...
林绍涛为简艾拿绿豆糕是哪一集 林绍涛为简艾拿绿豆糕是哪一集第三十二集。 贾宽认为是阎帅间接导致刘映霞住了院,第二天上班,他按捺不...
小爱同学是女生吗小安同学什么意... 小爱同学是女生吗小安同学什么意思 小爱同学,小安同学说你是女生。小安是男的。
内分泌失调导致脸上长斑,怎么调... 内分泌失调导致脸上长斑,怎么调理内分泌失调导致脸上长斑,怎么调理先调理内分泌,去看中医吧,另外用好的...
《魔幻仙境》刺客,骑士人物属性... 《魔幻仙境》刺客,骑士人物属性加点魔幻仙境骑士2功1体质
很喜欢她,该怎么办? 很喜欢她,该怎么办?太冷静了!! 太理智了!爱情是需要冲劲的~不要考虑着考虑那~否则缘...
言情小说作家 言情小说作家我比较喜欢匪我思存的,很虐,很悲,还有梅子黄时雨,笙离,叶萱,还有安宁的《温暖的玄》 小...
两个以名人的名字命名的风景名胜... 两个以名人的名字命名的风景名胜?快太白楼,李白。尚志公园,赵尚志。
幼儿教育的代表人物及其著作 幼儿教育的代表人物及其著作卡尔威特的《卡尔威特的教育》,小卡尔威特,他儿子成了天才后写的《小卡尔威特...
海贼王中为什么说路飞打凯多靠霸... 海贼王中为什么说路飞打凯多靠霸气升级?凯多是靠霸气升级吗?因为之前刚到时确实打不过人家因为路飞的实力...
运气不好拜财神有用吗运气不好拜... 运气不好拜财神有用吗运气不好拜财神有没有用1、运气不好拜财神有用。2、拜财神上香前先点蜡烛,照亮人神...