当前位置: 首页 > 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…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...