当前位置: 首页 > news >正文

SSM框架-AOP概述、Spring事务

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<Account> 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<Account> 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<Account> findAll() {return accountDao.findAll();}
}

16.2 整合

1. 分析

在这里插入图片描述

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

sqkSessionFactory是核心对象

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

  1. pom准备
 <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><!--spring操作与数据库有关的--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency><!--spring整合一个mybits的jar包--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency>
  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准备
    <!--junit包--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--spring整合junit--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.10.RELEASE</version></dependency>
  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的包

    <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency>
  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<Account> 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<Account> 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<Account> 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<Account> 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<Account> 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
 <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency>
  • 配置类
@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. 配置项

在这里插入图片描述

相关文章:

SSM框架-AOP概述、Spring事务

16 spring整合mybatis 16.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 …...

一文搞定Android Vsync原理简析

屏幕渲染原理"现代计算机之父"冯诺依曼提出了计算机的体系结构: 计算机由运算器&#xff0c;存储器&#xff0c;控制器&#xff0c;输入设备和输出设备构成&#xff0c;每部分各司其职&#xff0c;它们之间通过控制信号进行交互。计算机发展到现在&#xff0c;已经出…...

第八届蓝桥杯省赛 C++ B组 - K 倍区间

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;K 倍区间 &#x1f4e3;专栏定位&#xff1a;为想参加蓝桥杯的小伙伴整理常考算法题解&#xff0c;祝大家…...

UDP与TCP协议

目录 UDP协议 协议报头 UDP协议特点&#xff1a; 应用场景&#xff1a; TCP TCP协议报头 确认应答机制 理解可靠性 超时重传机制 连接管理机制 三次握手&#xff1a; 四次挥手&#xff1a; 滑动窗口 如何理解缓冲区和滑动窗口&#xff1f; 倘若出现丢包&#xf…...

rosbag相关使用工具

文章目录一、 rosbag 导出指定话题生成新rosbag二、 rosbag 导出视频1. 脚本工具源码2. 操作2.1 安装 ffmpeg2.2 导出视频3. 视频截取4. 压缩视频附录&#xff1a;rosbag2video.py 源码一、 rosbag 导出指定话题生成新rosbag rosbag filter 2023-02-25-19-16-01.bag depth.bag…...

数据结构与算法—栈stack

目录 栈 栈的复杂度 空间复杂度O(1) 时间复杂度O(1) 栈的应用 1、栈在函数调用中的应用&#xff1b; 2、栈在求表达式的值的应用&#xff1a; 栈的实现 栈 后进先出&#xff0c;先进后出&#xff0c;只允许在一端插入和删除 从功能上&#xff0c;数组和链表可以代替栈…...

【学习笔记】[ARC150F] Constant Sum Subsequence

第一眼看上去&#xff0c;这道题一点都不套路 第二眼看上去&#xff0c;大概是要考dpdpdp优化&#xff0c;那没事了&#xff0c;除非前面333道题都做完了否则直接做这道题肯定很亏 首先我们要定义一个好的状态。废话 设fsf_{s}fs​表示BBB序列的和为sss时&#xff0c;能达到…...

Node.js实现大文件断点续传—浅析

Node.js简介&#xff1a; 当谈论Node.js时&#xff0c;通常指的是一个基于Chrome V8 JavaScript引擎构建的开源、跨平台的JavaScript运行时环境。以下是一些Node.js的内容&#xff1a; 事件驱动编程&#xff1a;Node.js采用了事件驱动的编程范式&#xff0c;这意味着它可以异步…...

Spring Cloud Nacos源码讲解(九)- Nacos客户端本地缓存及故障转移

Nacos客户端本地缓存及故障转移 ​ 在Nacos本地缓存的时候有的时候必然会出现一些故障&#xff0c;这些故障就需要进行处理&#xff0c;涉及到的核心类为ServiceInfoHolder和FailoverReactor。 ​ 本地缓存有两方面&#xff0c;第一方面是从注册中心获得实例信息会缓存在内存当…...

MySQL知识点小结

事务 进行数据库提交操作时使用事务就是为了保证四大特性,原子性,一致性,隔离性,持久性Durability. 持久性:事务一旦提交,对数据库的改变是永久的. 事务的日志用于保存对数据的更新操作. 这个操作T1事务操作的会发生丢失,因为最后是T2提交的修改,而且T2先进行一次查询,按照A…...

MySQL关于NULL值,常见的几个坑

数据库版本MySQL8。 1.count 函数 觉得 NULL值 不算数 &#xff0c;所以开发中要避免count的时候丢失数据。 如图所示&#xff0c;以下有7条记录&#xff0c;但是count(name)却只有6条。 为什么丢失数据&#xff1f;因为MySQL的count函数觉得 Null值不算数&#xff0c;就是说…...

OllyDbgqaqazazzAcxsaZ

本文通过吾爱破解论坛上提供的OllyDbg版本为例&#xff0c;讲解该软件的使用方法 F2对鼠标所处的位置打下断点&#xff0c;一般表现为鼠标所属地址位置背景变红F3加载一个可执行程序&#xff0c;进行调试分析&#xff0c;表现为弹出打开文件框F4执行程序到光标处F5缩小还原当前…...

Elasticsearch7.8.0版本进阶——自定义分析器

目录一、自定义分析器的概述二、自定义的分析器的测试示例一、自定义分析器的概述 Elasticsearch 带有一些现成的分析器&#xff0c;然而在分析器上 Elasticsearch 真正的强大之 处在于&#xff0c;你可以通过在一个适合你的特定数据的设置之中组合字符过滤器、分词器、词汇单 …...

spring事务-创建代理对象

用来开启事务的注解EnableTransactionManagement上通过Import导入了TransactionManagementConfigurationSelector组件&#xff0c;TransactionManagementConfigurationSelector类的父类AdviceModeImportSelector实现了ImportSelector接口&#xff0c;因此会调用public final St…...

Linux 配置NFS与autofs自动挂载

目录 配置NFS服务器 安装nfs软件包 配置共享目录 防火墙放行相关服务 配置NFS客户端 autofs自动挂载 配置autofs 配置NFS服务器 nfs主配置文件参数&#xff08;/etc/exports&#xff09; 共享目录 允许地址1访问&#xff08;选项1&#xff0c;选项2&#xff09; 循序地…...

【编程入门】应用市场(Python版)

背景 前面已输出多个系列&#xff1a; 《十余种编程语言做个计算器》 《十余种编程语言写2048小游戏》 《17种编程语言10种排序算法》 《十余种编程语言写博客系统》 《十余种编程语言写云笔记》 《N种编程语言做个记事本》 目标 为编程初学者打造入门学习项目&#xff0c;使…...

异常信息记录入库

方案介绍 将异常信息放在日志里面&#xff0c;如果磁盘定期清理&#xff0c;会导致很久之前的日志丢失&#xff0c;因此考虑将日志中的异常信息存在表里&#xff0c;方便后期查看定位问题。 由于项目是基于SpringBoot构架的&#xff0c;所以采用AdviceControllerExceptionHand…...

Spring Batch 高级篇-分区步骤

目录 引言 概念 分区器 分区处理器 案例 转视频版 引言 接着上篇&#xff1a;Spring Batch 高级篇-并行步骤了解Spring Batch并行步骤后&#xff0c;接下来一起学习一下Spring Batch 高级功能-分区步骤 概念 分区&#xff1a;有划分&#xff0c;区分意思&#xff0c;在…...

ES数据迁移_snapshot(不需要安装其他软件)

参考文章&#xff1a; 三种常用的 Elasticsearch 数据迁移方案ES基于Snapshot&#xff08;快照&#xff09;的数据备份和还原CDH修改ElasticSearch配置文件不生效问题 目录1、更改老ES和新ES的config/elasticsearch.yml2、重启老ES&#xff0c;在老ES执行Postman中创建备份目录…...

【Vue3 第二十章】异步组件 代码分包 Suspense内置组件 顶层 await

异步组件 & 代码分包 & Suspense内置组件 & 顶层 await 一、概述 在大型项目中&#xff0c;我们可能需要拆分应用为更小的块&#xff0c;以减少主包的体积&#xff0c;并仅在需要时再从服务器加载相关组件。这时候就可以使用异步组件。 Vue 提供了 defineAsyncC…...

「媒体邀约」四川有哪些媒体,成都活动媒体邀约

传媒如春雨&#xff0c;润物细无声&#xff0c;四川省位于中国西南地区&#xff0c;是中国的一个省份。成都市是四川省的省会&#xff0c;成都市是中国西部地区的政治、经济、文化和交通中心&#xff0c;也是著名的旅游胜地。每年的文化交流活动很多&#xff0c;也有许多的大企…...

@Autowired和@Resource的区别

文章目录1. Autowired和Resource的区别2. 一个接口多个实现类的处理2.1 注入时候报错情况2.2 使用Primary注解处理2.3 使用Qualifer注解处理2.4 根据业务情况动态的决定注入哪个serviceImpl1. Autowired和Resource的区别 Aurowired是根据type来匹配&#xff1b;Resource可以根…...

Linux系列:glibc程序设计规范与内存管理思想

文章目录前言命名规范说明版式风格内存管理与智能指针关于UML前言 这是一个基于lightdm、glibc、gobject、gtk、qt、glibc、x11、wayland等多个高质量开源项目总结而来的规范。 glibc处于内核态与用户态的边界&#xff0c;承上启下&#xff0c;对用户的体验影响非常大。其在系…...

Redis 集群

文章目录一、集群简介二、Redis集群结构设计&#x1f349;2.1 数据存储设计&#x1f349;2.2 内部通信设计三、cluster 集群结构搭建&#x1f353;3-1 cluster配置 .conf&#x1f353;3-2 cluster 节点操作命令&#x1f353;3-3 redis-trib 命令&#x1f353;3-4 搭建 3主3从结…...

EF 框架的简介、发展历史;ORM框架概念

一、EF 框架简介EF 全称是 EntityFramework 。Entity Framework是ADO.NET 中的一套支持开发面向数据的软件应用程序的技术,是微软的一个ORM框架。ORM框架&#xff08;Object Relational Mapping&#xff09; 翻译过来就是对象关系映射。如果不用ORM框架&#xff0c;我们一般这样…...

注解原理剖析与实战

一、注解及其原理 1.注解的基本概念 注解&#xff0c;可以看作是对 一个类/方法的一个扩展的模版&#xff0c;每个类/方法按照注解类中的规则&#xff0c;来为类/方法注解不同的参数&#xff0c;在用到的地方可以得到不同的类/方法中注解的各种参数与值。 从JDK5开始&#xff…...

《STL源码剖析》理解之将类成员函数和for_each等算法结合

类成员函数可以通过函数适配器(function adapters)包装成一个仿函数(重载了operator()的类)&#xff0c;将其搭配于STL算法一起使用。#include <algorithm> #include <functional> #include <vector> #include <iostream>using namespace std;class In…...

如何构建应用标准化体系

标准化的过程实际上就是对运维对象的识别和建模过程。形成统一的对象模型后&#xff0c;各方在统一的认识下展开有效协作&#xff0c;然后针对不同的运维对象&#xff0c;再抽取出它们所对应的运维场景&#xff0c;接下来才是运维场景的自动化实现。 在标准化的过程中&#xf…...

【RabbitMQ笔记03】消息队列RabbitMQ七种模式之WorkQueues工作队列模式

这篇文章&#xff0c;主要介绍消息队列RabbitMQ七种模式之WorkQueues工作队列模式。 目录 一、工作队列模式 1.1、什么是Work Queues模式 1.2、工作队列模式的使用 &#xff08;1&#xff09;引入依赖 &#xff08;2&#xff09;编写生产者 &#xff08;3&#xff09;编写…...

认识html

1.html的特点先看一段简单的html代码<html><head></head><body>hello world</body> </html>如果将这段带有这段代码的.html文件拉进浏览器中,就会出现一个页面,内容就是hello world,如下图:由上面的代码,我们可以了解到一些html代码的特点…...