学习目标:
掌握spring框架学习内容:
1、 spring是什么 2、 spring优点 3、 spring搭建 4、 spring IOC 5、 spring AOP 6、 spring 事务管理 7、 spring 事务传播行为 8、 spring 集成mybatis学习时间:
1、 2021年11月7日 2、 2021年11月8日 3、 2021年11月10日 4、 2021年11月17日 5、 2021年11月19日 6、 2021年11月20日学习产出:
1、 技术笔记 1 遍 2、CSDN 技术博客 1 篇spring是什么
spring是于2003年兴起的一个轻量级的,IOC和AOP的java开发框架,它是为了简化企业级应用开发而生的.
优点
轻量级的
Spring框架使用的jar都比较小,一般在1M以下或者几百kb。
Spring核心功能所需的jar总在3M左右。
Spring框架运行占用的资源很小,运行效率高。
非侵入式的
UserSrlevt extends HttpServlet(侵入式)
编写一些业务类的时候不需要继承Spring特定的类,通过配置完成依赖注入后就可以使用,这样spring就没有入侵到我们的业务代码中。
IOC
即Inversion of Control ,缩写为IOC 意思为控制反转
把生成对象的控制权反转给spring框架 由spring框架生成并管理对象。
AOP
Aspect Oriented Programming 面向切面编程
通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术
对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
AOP的思想是 可以帮我们动态代理的调用,而不需要修改源代码
一站式
spring是一个后端管理架构框架,集基础容器,数据访问(jdbc,事物管理),web(Servlet),AOP为一身的一站式框架。
Spring搭建
maven导入spring核心基础jar
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
编写spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
开启spring注解扫描
-->
<context:component-scan base-package="com.ffyc.spring"></context:component-scan>
</beans>
编写实体类
@Component(value = "user") //想当于<bean id=“user” class=“”></bean>
@Scope(value = "singleton") //指定生成对象的策略(单例)
public class User {
private String name;
private Integer age;
List<Integer> list;
public User() {
/*每次创建对象会调用无参构造方法*/
System.out.println("user无参构造");
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", list=" + list +
'}';
}
}
测试spring
public static void main(String[] args) {
/*
ApplicationContext 是Spring的一个底层接口,定义一些规范
ClassPathXmlApplicationContext 是Spring的一个具体的落地实现类,创建并管理对象 里面的参数是配置文件
思想:IOC 把生成对象的控制权反转给Spring框架
在程序中需要对象的时候,直接从Spring容器中获取即可(依赖注入)
*/
ApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
/*//默认创建一个单例对象
User user1 = app.getBean("user",User.class);
// 通过别名创建对象
User user2 = app.getBean("user1",User.class);
System.out.println(user1);
System.out.println(user2);*/
//创建对象默认调用无参构造
User user1 = app.getBean("user",User.class);
System.out.println(user1);
}
IOC(控制反转)
Inverse of Control 读成反转控制更容易理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交给了spring框架来管理。
IOC容器是具有依赖注入功能的容器,负责对象的实例化、对象的初始化,对象和对象之间依赖关系配置、对象的销毁、对外提供对象的查找等操作,对象的整个生命周期都是由容器来控制。我们需要使用的对象都由IOC容器进行管理,不需要我们再去手动通过new的方式去创建对象,由IOC容器直接帮我们组装好,当我们需要使用的时候直接从IOC容器中直接获取就可以了。
正控
若要使用某个对象,需要自己去负责对象的创建
反控
若要使用某个对象,只需要从spring容器中获取需要使用的对象,不关心对象创建的过程,把创建对象的控制权反转给spring框架。
IOC目的
降低耦合度
底层实现方式
解析xml/扫描注解标签 + 工厂模式 + 反射机制
Spring Bean管理
这也是springIOC思想的体现
基于xml配置方式
bean 配置化需要spring管理的类
id 生成的对象名
class 全类名
name 对象别名,可以为多个
scope:
singleton(默认值):在 Spring 中只存在一个 bean 实例, 单例模式.
prototype:原型 getBean()的时候都会 new Bean()
request:每次 http 请求都会创建一个 bean, 仅用于 WebApplicationContext 环境
session:同一个 http session 共享一个 Bean, 不同 Session 使用不同的 Bean, 使用环境同上
<!--配置我们项目中的类,配置类之后Spring就会对对象进行对象的存储管理(生成对象存储)
由Spring框架生成的对象称之为一个bean,特指Spring框架创建的对象,会给一定特殊功能
id:生成对象的表示
class:类的地址(全类名)
name:对象别名,通过name也可以获取对象
scope:范围,作用域
singleton:单例的,整个应用程序中只创建一次,多次获取是同一个,在Spring容器启动时就创建
prototype:原型的(多例的) 每次获取时,会创建一个新的对象 -->
<bean id="user" name="user1,user2" class="com.ffyc.spring.model.User" scope="prototype"></bean>
xml配置方式依赖注入
DI:Dependency Injection(xml配置方式依赖注入)
指Spring创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设置给该对象。
实现IOC需要DI注入
有两种注入方式
1、构造方法注入
2、set方法注入
构造方法注入
使用较少
<!--通过属性名注入值-->
<bean id="user" class="com.ffyc.spring.model.User">
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="age" value="20"></constructor-arg>
</bean>
<!--通过属性的类型注入值-->
<bean id="user" class="com.ffyc.spring.model.User">
<constructor-arg type="java.lang.String" value="张三"></constructor-arg>
<constructor-arg type="java.lang.Integer" value="22"></constructor-arg>
</bean>
set方法注入
<bean id="user" class="com.ffyc.spring.model.User">
<property name="name" value="李四"></property>
<property name="age" value="21"></property>
<property name="list">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
</bean>
<!-- spring管理了UserDao UserService-->
<bean id="userDao" class="com.ffyc.spring.dao.UserDao"></bean>
<bean id="userService" class="com.ffyc.spring.service.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
注解方式实现
注解开发准备工作:
导入注解需要的jar包
注解需要的jar包封装在spring aop jar 包中
开启注解扫描
在spring的配置文件中开始注解扫描
<!--
开启spring注解扫描
-->
<context:component-scan base-package="com.ffyc.spring"></context:component-scan>
注解创建对象
@Component
@Service
@Repository
以上注解都可以实现创建对象的功能,知识为了后续扩展功能,在不同的层使用不同的注解标记
创建原型或者单例
@Scope(value = “prototype”) 原型
@Scope(value = “singleton”) 单例
@Component(value = "user") //想当于<bean id=“user” class=“”></bean>
@Scope(value = "singleton") //指定生成对象的策略(单例)
public class User
@Service(value = "userService")
public class UserService
@Repository(value = "userDao")
@Scope(value = "singleton") //指定生成对象的策略(单例)
public class UserDao
注解方式注入属性
@Autowired
@Autowired 是 Spring 提供的注解,可以写在字段和 setter 方法上。如果写在字段上,那么就不需要再写 setter 方法。默认情况下它要求依赖对象必须存在, 如果允许 null 值,可以设置它的 required 属性为 false。
byType 自动注入
该注解默认使用按类型自动装配 Bean 的方式
byName 自动注入如果我们想使用按照名称(byName)来装配,可以结合@Qualifier 注解一起使用。
需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用于指定要匹配的 Bean 的 id 值。
/*
@Autowired 是spring框架自身提供的注解标签
required = true 这是默认的 注入的时候不能为null值
是根据属性的类型自动注入(根据属性的类型在spring容器中查找)
也可以通过对象名查找注入,需要结合@Qualifier(value = "userDao")
*/
@Autowired(required = false)
@Qualifier(value = "userDao")
UserDao userDao;
JDK 注解@Resource 自动注入
Spring 提供了对 jdk 中@Resource 注解的支持。@Resource 注解既可以按名称匹配 Bean,也可以按类型匹配 Bean。默认按照 ByName 自动注入
byName 注入引用类型属性
@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。
//指定注入的对象名,不写就根据类型查找
@Resource(name = "userDao") //是JDK自身提供的注解标签,也支持byName和byType两种注入方式
UserDao userDao;
注解与 xml 的对比
注解优点
方便、直观、高效(代码少,没有配置文件的书写那么复杂)
注解缺点
以硬编码的方式写入到java代码中,修改是需要重新编译代码的。
xml优点
配置和代码是分离的,在xml中做修改,无需编译代码,只需要重启服务器即可将新的配置加载。
xml缺点
编写麻烦,效率低,大型项目过于复杂。
Spring JDBC
Spring 是个一站式框架:Spring 自身也提供了控制层的 SpringMVC 和 持久层的 Spring JdbcTemplate。
开发步骤
下载 Spring JdbcTemplate 的 jar 包
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<!-- 阿里数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
创建JDBC配置文件
db.xml
导入属性文件
<context:property-placeholder location="config.properties"/>
管理数据源对象
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${user}"></property>
<property name="password" value="${pwd}"></property>
<property name="initialSize" value="10"></property>
<property name="minIdle" value="5"></property>
<property name="maxActive" value="20"></property>
</bean>
在配置文件中创建 JdbcTemplate
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
在类中获取JdbcTemplate对象可以直接使用
@Autowired
JdbcTemplate jdbcTemplate;
JdbcTemplate 中常用的方法
execute:无返回值,可执行 ddl,增删改语句
update:执行新增、修改、删除语句;
queryForXXX:执行查询相关语句;
基本操作
插入数据,返回的结果为影响的条数
int row = jdbcTemplate.update("insert into admin (account,password,sex) value(?,?,?)","jim","111","男");
System.out.println("插入成功");
创建表
jdbcTemplate.execute("create table test(id int)");
查询
查询返回单个类型
int count = jdbcTemplate.queryForObject("select count(*) from admin", Integer.class);
通过传参查询单个对象,参数为一个数组,写内部类来映射返回的类型
Admin admin = jdbcTemplate.queryForObject("select * from admin where id = ?", new Object[]{2},
new RowMapper<Admin>() {
@Override
public Admin mapRow(ResultSet resultSet, int i) throws SQLException {
Admin admin = new Admin();
admin.setId(resultSet.getInt("id"));
admin.setAccount(resultSet.getString("account"));
admin.setPassword(resultSet.getString("password"));
admin.setSex(resultSet.getString("sex"));
return admin;
}
});
查询结果为map对象,map的键为列名,值为对应列的值
List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from admin");
System.out.println(maps);
查询返回对象的集合
List<Admin> list = jdbcTemplate.query("select * from admin",
new RowMapper<Admin>() {
@Override
public Admin mapRow(ResultSet resultSet, int i) throws SQLException {
Admin admin = new Admin();
admin.setId(resultSet.getInt("id"));
admin.setAccount(resultSet.getString("account"));
admin.setPassword(resultSet.getString("password"));
admin.setSex(resultSet.getString("sex"));
return admin;
}
});
System.out.println(list);
AOP概述
AOP (Aspect Oriented Programming) 面向切面编程
通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
AOP是OOP(面向整体设计)的延续。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
OOP
OOP (面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
AOP
针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果
AOP的好处
减少重复,专注业务
注意:面向切面编程只是面向对象编程的一种补充
核心思想:
使用动态代理的方式,在方法执行前后,或者出现异常时加入某种逻辑
可以实现在不修改代码的情况下,为程序添加新的功能
抽取的代码有条件(功能与业务逻辑没有直接关系)
使用案例
事务处理:开启事务,关闭事务,出现异常后回滚事务
权限判断:在执行方法前,判断是否具有权限
日志:在执行前进行日志处理
@Repository(value = "userDao")
@Scope(value = "singleton") //指定生成对象的策略(单例)
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
/*
事务:看做是对一组数据的若干操作组成的一个序列,是一个整体的过程,要么都成功,要么都不成功
jdbcTemplate 默认使用jdbc的事务提交方式,自动的,执行完自动提交
spring中提供的事务管理,实现原理是AOP思想
开发中,之前已经写好的一个功能,后来又要添加到新的功能进来(例如保存日志),那么我们必须要修改原来的代码
这样的话扩展性不强
*/
//如果在save里写了catch,就不会执行异常通知,因为异常已经在这里处理了
public void save(){
int a = 10/0;
System.out.println("保存用户");
}
}
public class AOPDemo {
//增强的逻辑
public void saveLog(){
System.out.println("保存日志");
}
}
实现是通过配置文件链接上,在后面会提到
AOP的基本概念
连接点(Joinpoint):类中可以被增强的方法,这个方法就被称为连接点
切入点(pointcut):类中有很多方法可以被增强,但实际中只有 add 和 update被增了,那么 add 和 update 方法就被称为切入点(实际实现的连接点)
@Repository(value = "userDao")
@Scope(value = "singleton") //指定生成对象的策略(单例)
public class UserDao {
/*
连接点(Joinpoint): 类中可以被用来增强的方法
切入点(Ponintcut): 实际被增强的方法
*/
public void save(){
int a = 10/0;
System.out.println("保存用户");
}
public void delete(){
System.out.println("删除用户");
}
}
通知(Advice):通知是指一个切面在特定的连接点要做的事情(增强的功能)。通知分为方法执行前通知,方法执行后通知,环绕通知等.
切面(Aspect):把通知添加到切入点的过程叫切面
/**
* 切面
*/
public class AOPDemo_back {
/*
通知:在连接点上要做的事情(增强的功能)
通知时又可以分为前置通知、后置通知、环绕通知、异常通知
*/
public void saveLog(){
System.out.println("保存日志");
}
}
目标(Target): 代理的目标对象(要增强的类)
/*
目标
*/
public class UserDao {
}
代理(Proxy):向目标对象通知之后创建的代理对象
Spring AOP 实现
对于AOP这种编程思想,很多框架都进行了实现。spring就是其中之一,可以完成面向切面编程。
AspectJ 也实现了 AOP 的功能,AspectJ 是一个基于 Java 语言的 AOP 框架,它提供了强大的 AOP 功能,且其实现方式更为简捷,使用更为方便, 而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
AspectJ 中常用的通知有五种类型:
前置通知,后置通知,环绕通知,异常通知,最终通知
下载相关jar
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
基于aspectj的xml配置方式实现
<!-- 把装有增强功能的类交给spring管理 -->
<bean id="aopdemo" class="com.ffyc.spring.aop.AOPDemo"></bean>
<!--编织: 将连接点与通知编织到一起 -->
<aop:config>
<!--配置切入点 为具体的哪个方法增强-->
<!-- execution 表达式的主题 *:返回类型 *代表所有返回类型 后面跟着包名,类名,方法名 类名、方法名也可以用*代表所有 ..表示任意参数 -->
<aop:pointcut id="save" expression="execution(* com.ffyc.spring.dao.UserDao.save(..))"/>
<!-- 配置通知和切入点 -->
<aop:aspect ref="aopdemo">
<!--<aop:before method="saveLog" pointcut-ref="save"></aop:before> 前置通知-->
<!--<aop:after method="saveLog" pointcut-ref="save"></aop:after> 后置通知-->
<!--<aop:after-returning method="saveLog" pointcut-ref="save"></aop:after-returning> 最终通知,在return执行后-->
<!--<aop:after-throwing method="exceptionAdvice" pointcut-ref="save" throwing="e"></aop:after-throwing> 异常通知-->
<!-- 环绕通知 -->
<aop:around method="aroundAdvice" pointcut-ref="save"></aop:around>
</aop:aspect>
</aop:config>
/*
通知:在连接点上要做的事情(增强的功能)
通知时又可以分为前置通知、后置通知、环绕通知、异常通知
*/
public void saveLog(){
System.out.println("保存日志");
}
/*
异常通知
*/
public void exceptionAdvice(Throwable e){
System.out.println(e.getMessage());
}
public void aroundAdvice(ProceedingJoinPoint point){
System.out.println("-----前置通知-----");
try {
point.proceed();//调用切入点方法
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println(throwable.getMessage());//异常通知
}
System.out.println("-----后置通知-----");
}
基于注解方式的实现
在spring配置文件中
启动 AspectJ 支持:<aop:aspectj-autoproxy />
<!--开启自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Component
@Aspect
public class AOPDemo {
/*//@Before("execution(* com.ffyc.spring.dao.UserDao.save(..))") //前置通知
@After("execution(* com.ffyc.spring.dao.UserDao.save(..))") // 后置通知
public void saveLog(){
System.out.println("保存日志");
}
@AfterThrowing(value = "execution(* com.ffyc.spring.dao.UserDao.save(..))",throwing = "e") // 异常通知
public void exceptionAdvice(Throwable e){
System.out.println(e.getMessage());
}*/
@Around("execution(* com.ffyc.spring.dao.UserDao.save(..))")
public void aroundAdvice(ProceedingJoinPoint point){
System.out.println("-----前置通知-----");
try {
point.proceed();//调用切入点方法
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println(throwable.getMessage());//异常通知
}
System.out.println("-----后置通知-----");
}
}
spring事务管理
事物可以看做是由对数据库若干操作组成的一个单元。
我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作 的结合。由于数据操作在顺序执行的过程中,任何一步操作都有可能发生异常, 异常会导致后续操作无法完成,此时由于业务逻辑并未正确的完成,之前成功操作数据的并不可靠,需要在这种情况下进行回退。
事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态,这些操作要么都完成,要么都取消,从而保证数据满足一致性的要求
Spring 中的事务管理分为两种形式,一种是编程式事务,一种是声明式事务
编程式事务
在 项 目 中 很 少 使 用 , 这 种 方 式 需 要 注 入 一 个 事 务 管 理 对 象 TransactionTemplate
然后在我们代码中需要提交事务或回滚事务时自己写代码实现
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
TransactionTemplate t; //编程式事务,在我们的代码中需要句mybatis那样手动(显示)的提交回滚事务
/*
事务:数据库事务是对数据库若干操作组成的一个单元(整体)
在我们的一次save时,里面包含了两个或者更多个对数据库的操作
那么我们认为这若干个操作应该是一个单元(整体)
这就是事务的原子性(不可分割性),多个操作要么都执行成功,要么都不执行
*/
public void save1(){
//t.execute(""); 使用t的方法实现编程式事物
jdbcTemplate.update("insert into admin(account,password,sex)values(?,?,?)","jim1","111","男");
int a = 10/0;
jdbcTemplate.update("insert into admin(account,password,sex)values(?,?,?)","jim2","111","男");
}
声明式事务
声明式事务管理建立在 AOP 基础上,本质是对方法前后进行拦截,所以声明式事务是方法级别的
Spring 声明式事物管理方式有两种:
基于 xml 配置
基于注解实现
Spring 事物管理 APIPlatformTransactionManager 事物管理器接口
Spring 针对不同的 dao 框架,提供了不同的实现类,Jdbc,mybatis 事物管理实现类是 DataSourceTransactionManager.
配置事物管理器
<!-- 配置 spring 事务管理类, 并注入数据源 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
Xml 配置方式
<!--配置spring事务传播行为, 这是spring框架自己特有的一个功能-->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.ffyc.spring.dao.UserDao.save())" id="save"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="save"/>
</aop:config>
注解方式
<!--注解方式实现事务管理配置-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
提出一个场景:
比如淘宝下单操作 一个下订单 一个支付
在dao层是两个方法,但是在service层只有一个方法
所以我们将事务管理交给service层
@Service(value = "userService")
@Transactional /* 添加在类上面,那么整个类的所有方法都会在事务管理中进行*/
public class UserService {
@Resource(name = "userDao")
UserDao userDao;
//@Transactional //如果添加在某个方法上,表示此方法在事务管理中进行
public void saveUser(){
userDao.save1();
int a = 10/0;
userDao.save2();
}
}
Spring事务传播行为
什么叫事务传播行为:
即然是传播,那么至少有两个东西,才可以发生传播。单体不存在传播这个行为。事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。事务传播行为是 Spring 框架独有的事务增强特性,他不属于的事务实际提供方数据库行为.
事务传播行为是 Spring 框架独有的事务增强特性,他不属于的事务实际提供方数据库行为.
例如:methodA 事务方法调用 methodB 事务方法时,methodB 是继续在调用者 methodA 的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB 的事务传播行为决定的。
spring定义了七种传播行为
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入到这个事务中,这是最常见的。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务 挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事 |
详细学习三种事务传播
PROPAGATION_REQUIRED
事务A,即调用其它方法的事务
@Transactional (propagation = Propagation.REQUIRED)
public void save(){
deptDao.save();
commonService.savelog();
}
事务B,即被调用方法的事务
/*
Propagation.REQUIRED
A方法传播行为REQUIRED,调用B方法,B方法传播行为也是REQUIRED的情况下,那么B事务就加入到A事务中执行
A方法无事务,调用B方法,B方法传播行为是REQUIRED的情况下,那么B方法会自己开启一个事务
*/
@Transactional(propagation = Propagation.REQUIRED)
public void savelog(){
commonDao.save();
int a = 10/0;
}
PROPAGATION_SUPPORTS
事务A,即调用其它方法的事务
@Transactional (propagation = Propagation.REQUIRED)
public void save(){
deptDao.save();
commonService.savelog();
}
事务B,即被调用方法的事务
/*
Propagation.SUPPORTS
A方法传播行为REQUIRED,调用B方法,B方法传播行为是SUPPORTS的情况下,那么B事务就加入到A事务中执行
A方法无事务,调用B方法,B方法传播行为是SUPPORTS的情况下,那么方法就以非事务方法执行
简单说成方法B不是独立的事务,跟随方法A的事务*/
@Transactional(propagation = Propagation.SUPPORTS)
public void savelog(){
commonDao.save();
int a = 10/0;
}
PROPAGATION_REQUIRES_NEW
事务A,即调用其它方法的事务
@Transactional (propagation = Propagation.REQUIRED)
public void save(){
deptDao.save();
commonService.savelog();
int a = 10/0;
}
事务B,即被调用方法的事务
/*
Propagation.REQUIRES_NEW
A方法传播行为REQUIRED,调用B方法,B方法传播行为是REQUIRES_NEW的情况下,那么B方法会开启一个独立的新的事务执行,B不会受到A的影响
A方法无事务,调用B方法,B方法传播行为是REQUIRES_NEW的情况下,那么B方法会开启一个独立的新的事务执行
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void savelog(){
commonDao.save();
}
声明式事务不生效的场景
同一个类中方法调用,导致@Transactional 失效
@Transactional 应用在非 public 修饰的方法上
@Transactional 注解属性 propagation 设置错误
异常被 catch 捕获导致@Transactional 失效
数据库引擎不支持事务
// 不使用public 默认使用default @Transactional就会失效,此方法就没有事务了
@Transactional (propagation = Propagation.REQUIRED)
void save(){
deptDao.save();
commonService.savelog();
}
在被调用方法添加catch补货
//这里的异常被捕获后,在调用处等于没有异常
@Transactional(propagation = Propagation.REQUIRED)
public void savelog(){
commonDao.save();
try{
int a = 10/0;
}catch (Exception e){
}
}
Spring集成mybatis
Spring 集成 Mybatis 其核心是将 SqlSessionFactory 交由 Spring 管理,并由 Spring 管理对 dao 接口的代理实现。
spring 项目的搭建和mubatis的搭建和之前一样
导入 mybatis jar 包
<!--spring-mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
添加相关配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fcXQ0Nnx-1637487288477)(E:\Users\asus\AppData\Roaming\Typora\typora-user-images\1637486607000.png)]
在spring_mybatis里面配置 sqlSessionFactory 并生成接口代理
<!--spring管理生成sqlSessionFactory读取mybatis各项配置文件-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mapper/*Mapper.xml"></property>
</bean>
<!--指定包下面的接口代理对象-->
<bean id="mapperFactory" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ffyc.ssm.dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
在service中注入dao代理接口,此借口由spring代理实现
@Service(value = "adminService")
public class AdminService {
@Autowired
AdminMapper adminMapper;
@Transactional
public void saveAdmin(){
adminMapper.saveAdmin();
}
}