spring 搭建项目环境
- 资源文件夹没有的给他加上

- 项目 jdk1.8
- file encoding utf-8
- 不用的文件可以删掉

1.添加spring依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.5.RELEASE</version></dependency>
2.资源路径(在spring中可以不用?但是为了养成习惯还是加上)
<resources>
<resource>
<directory>src/mian/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/mian/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
基于xml文件的IOC
通过容器创建第一个对象并取出
applicationContext.xml
<!-- 创建学生对象
等同于 Student student = new Student();
id:创建对象的名称
class:创建对象的类型(全类名) 底层通过 反射构建对象
启动容器的同时创建对象-->
<bean id="student" class="com.bjpowernode.entity.Student"></bean>
Mytest
@Test
//通过容器创建对象
public void student02(){
// 先创建对象 再取出对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 取出对象
Student studnet =(Student) context.getBean("student");
}
// 打印对象信息
System.out.println(studnet);
使用setter注入
setter 简单类型注入
- 注分为简单类型注入(八大基本数据类型 和String) 以及引用类型注入
- 简单类型注入使用value 属性
- 引用类型注入使用ref属性
- 注意:使用setter注入 必须要在类中给出setter 方法 和无参构造方法不然要报错的
1.xml文件
<bean id="student" class="com.bjpowernode.entity.Student">
<property name="name" value="张三"/>
<!-- 就算是数值类型也是要 用“ ” 这是xml的格式-->
<property name="age" value="18"/>
</bean>
2.MyTest
@Test
//通过容器创建对象
public void student02(){
// 先创建对象 再取出对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 取出对象
Student studnet =(Student) context.getBean("student");
// 打印对象信息
System.out.println(studnet);//学生{name='张三', age=18}
}
setter 引用类型注入


1.school
2.studnet
public class Student {
private String name;
private int age;
private School school;
public Student() {
System.out.println("student 学生类的无参构造。。。");
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
.......
3.application.xml
启动容器的同时创建对象-->
<bean id="school" class="com.bjpowernode.entity01.School">
<property name="name" value="重庆工商大学"/>
<!-- 就算是数值类型也是要 用“ ” 这是xml的格式-->
<property name="adress" value="重庆市南岸区"/>
</bean>
<!-- 创建学生加入 学校属性
xml文件是一个bean工厂 对象加入引用类型要 用ref 并且是之前已经创建过得对象
在xml 文件当中不分先后顺序-->
<bean id="student" class="com.bjpowernode.entity01.Student">
<property name="name" value="李四"></property>
<property name="age" value="21"></property>
<property name="school" ref="school"></property>
</bean>
4.MyTest
@Test
public void student (){
// 这里要注意 xml文件的路径
ApplicationContext ac = new ClassPathXmlApplicationContext("s02/applicationContext.xml");
Student student = (Student) ac.getBean("student");
System.out.println(student);
}
使用构造方法注入
使用构造方法的参数名进行注入
1.applicationContext.xml
<!--使用构造方法参数 参数名称注入值-->
<bean id="school" class="com.bjpowernode.entity02.School">
<!-- 这里的name 是构造方法中Strin name 的参数名 而不是 school 的属性名-->
<constructor-arg name="name" value="清华大学"></constructor-arg>
<constructor-arg name="adress" value="海淀区"></constructor-arg>
</bean>
通过构造方法参数的下标index 进行注入
<!--创建student 对象,用 构造方法参数下标-->
<bean id="student" class="com.bjpowernode.entity02.Student">
<constructor-arg index="0" value="菜菜"></constructor-arg>
<constructor-arg index="1" value="22"></constructor-arg>
<constructor-arg index="2" ref="school"></constructor-arg>
</bean>
通过构造方法参数的默认属性进行注入
<!-- 创建student 对象 用构造方法属性的默认顺序(顺序不可变)-->
<bean id="studentOrder" class="com.bjpowernode.entity02.Student">
<constructor-arg value="大菜"></constructor-arg>
<constructor-arg value="18"></constructor-arg>
<constructor-arg ref="school"></constructor-arg>
</bean>
- 构造方法
三层架构(案例)
使用传统的三层架构
spring02引入 spring 容器
spring02-xml基于注解的IOC(也叫DI)
使用@Component 注解创建对象

1.applicationContext。xml
2.studnent类 ```java<!-- @component只是 有一个标识可以注解生成对象 ,要实际生成还要包扫描!!!--> <context:component-scan base-package="com.bjpowernode.s01"></context:component-scan>
//注解生成对象默认是类名,可以修改对象名@Component(“修改的对象名”) @Component public class Student { private String name; private int age;
public Student() {
System.out.println("studenr的 无参构造方法");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
3.test类
```java
@Test
public void Test (){
ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
Student student = (Student) ac.getBean("student");
System.out.println(student);
}
简单类型注入
引用类型注入
引用类型按类型注入
school类
@Component
public class School {
@Value("清华大学")
private String name ;
@Value("海淀区")
private String age;
public School() {
System.out.println("school 的无参构造");
}
@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
student
@Component
public class Student {
@Value("李四")
private String name;
@Value("18")
private String age;
// 引用类型用autoweird
@Autowired
private School school;
public Student() {
System.out.println("student的无参构造方法");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", school=" + school +
'}';
}
}
-
引用类型按名称注入
school.java ```java @Component(“qinghua”)//按名称注入,这里的名字跟后面的 @Qualifier(“qinghua”)一样 public class School { @Value(“清华大学”) private String name ; @Value(“海淀区”) private String age;
public School() {
System.out.println("school 的无参构造");}
@Override public String toString() {
return "School{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}';} }
student.java
```java
@Component("lisi")
public class Student {
@Value("李四")
private String name;
@Value("18")
private String age;
// 引用类型用autoweird
@Autowired //这个autowired不能少 不然不能注入引用类型school的值
@Qualifier("qinghua")
private School school;
public Student() {
System.out.println("student的无参构造方法");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", school=" + school +
'}';
}
父子关系下 按名称注入和按类型注入
1.当存在父子关系的情况下,使用类型注入 那意味着有多个可注入的对象,此时会按名称进行儿子筛选(最终选择与被注入对象属性名一样的对象,例如school)

2.当存在父子关系的时候,最好用@Qualifier 直接用名称进行注入
- 父类

- 子类

- 被注入对象

- 结果

- 注:无论注入父类还是子类 子类的无参构造从都是会调用的
基于注解的三层项目改造
java代码 ```java @Repository public class UserMapperImpl implements UsersMapper {
@Override
public int addUsers(Users users) {
System.out.println(users.getUname()+"创建了");
return 1;
}
}
@Service public class UsersServiceImpl implements UsersService { //在业务层里面调用 数据访问层一定要创建对象 @Autowired private UsersMapper usersMapper = new UserMapperImpl();
@Override
public int addUsers(Users users) {
return usersMapper.addUsers(users);
}
}
@Controller public class UsersController {
// 在控制层里面处理业务 要创建 业务对象 @Autowired public UsersService usersService = new UsersServiceImpl(); public int addUsers(Users users){ return usersService.addUsers(users); } }
<a name="f8V3W"></a>
# 包扫描的方式
1)单个包扫描(推荐使用)<br /> <context:component-scan base-package="com.bjpowernode.controller"></context:component-scan><br /> <context:component-scan base-package="com.bjpowernode.service.impl"></context:component-scan><br /> <context:component-scan base-package="com.bjpowernode.dao"></context:component-scan>
2)多个包扫描,多个包之间以逗号或空格或分号分隔<br /> <context:component-scan base-package="com.bjpowernode.controller com.bjpowernode.service ,com.bjpowernode.dao"></context:component-scan>
3)扫描根包(不推荐)<br /> <context:component-scan base-package="com.bjpowernode"></context:component-scan><br /> 会降低容器启动的速度,导致多做无用功.
<a name="ik291"></a>
# 为应用指定多个spring配置文件
1)拆分配置文件的策略<br /> A.按层拆<br /> applicationContext_controller.xml <br /> <bean id="uController" class="com.bjpowernode.controller.UsersController"><br /> <bean id="bController" class="com.bjpowernode.controller.BookController"><br /> applicationContext_service.xml <br /> <bean id="uService" class="com.bjpowernode.controller.UsersService"><br /> <bean id="bService" class="com.bjpowernode.controller.BookService"><br /> applicationContext_mapper.xml <br /> <bean id="uMapper" class="com.bjpowernode.controller.UsersMapper"><br /> <bean id="bMapper" class="com.bjpowernode.controller.BookMapper">
B.按功能拆<br /> applicationContext_users.xml <br /> <bean id="uController" class="com.bjpowernode.controller.UsersController"><br /> <bean id="uService" class="com.bjpowernode.controller.UsersService"><br /> <bean id="uMapper" class="com.bjpowernode.controller.UsersMapper"><br /> applicationContext_book.xml <br /> <bean id="bController" class="com.bjpowernode.controller.BookController"><br /> <bean id="bService" class="com.bjpowernode.controller.BookService"><br /> <bean id="bMapper" class="com.bjpowernode.controller.BookMapper">
<a name="i0lDn"></a>
# 基于xml文件下的配置文件拆分
1.整合文件 名 一定要区别与其他文件名<br /><br />2.报红影响 一样可以运行<br />
3.整合文件(单个)<br />
```xml
<import resource="applicatoinContext_mapper.xml"></import>
<import resource="applicatoinContext_service.xml"></import>
<import resource="applicatoinContext_controller.xml"></import>
4.整合文件批量导入
<import resource="applicatoinContext_*.xml"></import>
基于xml文件引用类型的自动注入
applicationContext.xml
<bean id="student" class="com.bjpowernode.entity01.Student" AutoWeird="byType">
<property name="name" value="李四"></property>
<property name="age" value="21"></property>
// <property name="school" ref="school"></property>
</bean>
基于注解的配置文件拆分
面向切面编程AOP
切面:公共的、通用的、重复的功能成为切面,面向切面就是把切面提取出来单独开发,在调用方法中通过动态代理的方式进行植入。
1.静态代理


Agent.java ```java public class Agent extends BookServiceImpl{ //设计成员变量的类型为接口,可以灵活切换对象目标 public Service target; //用构造方法传入目标对象 public Agent(Service target) {
this.target = target;}
@Override public void buy(){
try {//切面功能
System.out.println("事务开启。。。");// 主业务
target.buy();// 事务关闭
System.out.println("事务关闭。。。"); } catch (Exception exception) { System.out.println("事务回滚。。。"); }} }
- test.java
2.
<a name="aRvHm"></a>
## 多个切面
```java
@Test
public void test01(){
Service service = new Agent(new BookServiceImpl(),new TransAop());
Service service1 = new Agent(service,new LogAop());
service1.buy();
aop动态代理
public class ProxyFactory {
public static Object getAgent(Service target,AOP aop){
return Proxy.newProxyInstance(
// 类加载
target.getClass().getClassLoader(),
// 目标对象实现的所有接口
target.getClass().getInterfaces(),
// 代理功能实现
new InvocationHandler() {
@Override
public Object invoke(
// 生成的代理对象
Object proxy,
//正在被调用的目标方法
Method method,
//目标参数的方法
Object[] args) throws Throwable {
Object obj=null;
// 切面
try {
aop.before();
// 业务
obj= method.invoke(target,args);
// 切面
aop.after();
} catch (Exception exception) {
aop.excepton();
}
// 切面
return obj;
}
}
);
}
}
AOP 常用术语
切入点表达式
1.规范的公式:
execution(访问权限 方法的返回值 方法声明(参数) 异常参数)
简化之后的公式:
execution (方法返回值 方法声明(参数))
用到的符号:
* 代表任意个的字符(通配符)
.. 如果在方法的参数中,表示任意参数
如果出现在路径中,代表本路径极其子路径
前置通知实现
切面方法实现前置通知
1.SomeService接口
public interface SomeService {
String doSome(String name ,int age);
}
2.SomeService接口
//业务实现类
public class SomeServiceImpl implements SomeService {
@Override
public String doSome(String name, int age) {
System.out.println("dosome的业务实现类。。。");
return "abcd";
}
}
3.test类
@Aspect //交给aspectj框架去识别切面类
public class MyAspect {
/**
* 所有切面的功能都是切面方法实现的
* 可以将各种切面都在此类中进行开发
*
*
* 前置通知的切面方法的规范
* 1.访问权限是public
* 2.方法的返回值是void
* 3.方法名称自定义
* 4.方法没有参数,如果有也只能是joinPoint类型
* 5.必须使用@Before注解来声明切入的时机是前切入功能和切入点
* 参数value 指定切入点表达式
*
* 业务方法
* String doSome(String name ,int age);
*/
@Before(value = "execution(public String com.bjpowernode.s01.SomeServiceImpl.doSome(String,int))")
public void MyBefore(){
System.out.println("切面方法中的前置通知功能实现。。。");
}
}
注解方法实现前置通知
1.application.xml
<!-- 用注解首先进行包扫描-->
<context:component-scan base-package="com.bjpowernode.s01"></context:component-scan>
2.mytest
@Test
public void test01(){
ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
SomeServiceImpl someService = (SomeServiceImpl) ac.getBean("someServiceImpl");
System.out.println(someService.getClass());
someService.show();
}
前置方法参数joinPoint(他是一个接口有实现类)
@Before(value = "execution( * com.bjpowernode.s01.*.*(..))")
public void MyBefore(JoinPoint joinPoint){
System.out.println("切面方法中的前置通知功能实现。。。");
// 获取参数
System.out.println(Arrays.toString(joinPoint.getArgs()));
// 获取标签
System.out.println(joinPoint.getSignature());
}
切入点表达式的各种用法
1.someService.jva
public interface SomeService {
String doSome(String name ,int age);
void show();
}
2.myaspects
@Before(value = "execution( * com.bjpowernode.s01.*.*(..))")
public void MyBefore(){
System.out.println("切面方法中的前置通知功能实现。。。");
}

3.myTest.java
public class MyTest {
@Test
public void test(){
ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
SomeService someService = (SomeService) ac.getBean("someService");
someService.show();
}
}
JDK动态代理与CGLib动态代理切换
1.jdk动态代理
public class MyTest {
@Test
public void test(){
ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
SomeService someService = (SomeService) ac.getBean("someService");
someService.show();
}
}
//这里转换的类型是接口对象 而不是接口实现类的对象如果用实现类的对象来接会报错

application.xml
<!--创建业务对象-->
<bean id="someService" class="com.bjpowernode.s01.SomeServiceImpl"></bean>
<!-- 创建切面对象-->
<bean id="myaspect" class="com.bjpowernode.s01.MyAspect"></bean>
<!-- 绑定-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
//用接口来接
3.控制台
2.CGLib动态代理模式
xml文件
<!-- CGlib动态代理-->
<!--创建业务对象-->
<bean id="someService" class="com.bjpowernode.s01.SomeServiceImpl"></bean>
<!-- 创建切面对象-->
<bean id="myaspect" class="com.bjpowernode.s01.MyAspect"></bean>
<!-- 绑定-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
//可以用接口实现类来接 也可以用接口来接
MyTest.java
@Test
public void test01(){
ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
SomeServiceImpl someService = (SomeServiceImpl) ac.getBean("someService");
System.out.println(someService.getClass());
someService.show();
}
}
后置通知实现
基本功能
1.myaspect.java
@Aspect
@Component
public class Myaspect {
/**1.访问权限是public
* 2.返回值是void
* 3.方法有没有参数取决于目标方法有没有返回值
* 4.使用@AfterReturning 注解表示后置通知
* 参数:
* value:指定切入点表达式
* returning 指定目标方法的返回值 名称与切面方法的参数名称一致
*/
@AfterReturning(value = "execution(* com.bjpowernode.s02.*.*(..))",returning = "obj")
public void myAfterReturning (Object obj){
System.out.println("后置通知功能实现。。。。");
if (obj!=null){
if (obj instanceof String ){
obj = obj.toString().toUpperCase();
System.out.println("将返回值改为大写:"+obj);
}
if (obj instanceof Student){
Student student = (Student) obj;
student.setName("李四");
System.out.println("学生的姓名是:"+student);
}
}
}
}
后置通知中不能改变返回值(简单类型)以及可以改变简单类型的情况(引用类型)
1.serviceimpl
@Service
public class SomeServiceImpl implements SomeService {
@Override
public String dosome(String name, int age) {
System.out.println("切面方法实施了。。。。。");
return "abcd";
}
@Override
public Student change() {
return new Student("张三");
}
}
2.myaspect
@Aspect
@Component
public class Myaspect {
/**1.访问权限是public
* 2.返回值是void
* 3.方法有没有参数取决于目标方法有没有返回值
* 4.使用@AfterReturning 注解表示后置通知
* 参数:
* value:指定切入点表达式
* returning 指定目标方法的返回值 名称与切面方法的参数名称一致
*/
@AfterReturning(value = "execution(* com.bjpowernode.s02.*.*(..))",returning = "obj")
public void myAfterReturning (Object obj){
System.out.println("后置通知功能实现。。。。");
if (obj!=null){
if (obj instanceof String ){
obj = obj.toString().toUpperCase();
System.out.println("将返回值改为大写:"+obj);
}
if (obj instanceof Student){
Student student = (Student) obj;
student.setName("李四");
System.out.println("学生的姓名是:"+student);
}
}
}
}
3.Test
public class MyTest02 {
@Test
public void myTest(){
ApplicationContext ac = new ClassPathXmlApplicationContext("s02/applicationContext.xml");
SomeService someServiceImpl = (SomeService) ac.getBean("someServiceImpl");
String s = someServiceImpl.dosome("张三", 22);
System.out.println(s);
}
@Test
public void myTest01(){
ApplicationContext ac = new ClassPathXmlApplicationContext("s02/applicationContext.xml");
SomeService someServiceImpl = (SomeService) ac.getBean("someServiceImpl");
Student s = someServiceImpl.change();
System.out.println(s);
}
}
环绕通知实现
通知实现
myaround
@Aspect
@Component
public class MyAround {
/**
* 环绕通知方法的规范
* 1.访问权限是public
* 2.切面方法有返回值,此返回值就是目标方法的返回值
* 3.方法名称自定义
* 4.方法有参数,参数就是目标方法
* 5.回避异常Throwable
* 6.用@Around注解什么是环绕通知 value指定切入点表达式
*
*
*/
@Around(value = "execution(* com.bjpowernode.s03.*.*(..))")
public Object myAround(ProceedingJoinPoint pjp)throws Throwable{
// 前切功能实现
System.out.println("环绕通知中的前置功能实现。。。。");
// 目标方法调用
Object obj = pjp.proceed(pjp.getArgs());
// 后切功能实现
System.out.println("环绕通知中的后置功能实现。。。");
// 改变目标方法的返回值
return obj.toString().toUpperCase();
}
}
最终通知
- 无论功能是否正常执行,最终通知都会 执行
```java
@Aspect
@Component
public class MyAfter {
/**
- 最终通知方法的规范
- 1.访问权限是public
- 2.方法没有返回值
- 3.方法名称自定义
- 4.方法没有参数,有也只能是joinpoint
- 参数:value(切入点) / @After(value = “execution( com.bjpowernode.s04..(..))”) public void myAfter(){ System.out.println(“最终通知功能。。。”); } }

<a name="ZEXQo"></a>
## 为一个方法添加多重切面(通知)
aspect
```java
@After(value = "execution(* com.bjpowernode.s04.*.*(..))")
public void myAfter(){
System.out.println("最终通知功能。。。");
}
@Before(value = "execution(* com.bjpowernode.s04.*.*(..))")
public void myBefore(){
System.out.println("前置通知功能通知功能。。。");
}
@AfterReturning(value = "execution(* com.bjpowernode.s04.*.*(..))",returning = "obj")
public void myAfterReturning(Object obj){
System.out.println("后置通知功能通知功能。。。");
}
@Around(value = "execution(* com.bjpowernode.s04.*.*(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知中的前置通知功能通知功能。。。");
Object obj = pjp.proceed(pjp.getArgs());
System.out.println("环绕通知中的后置通知的功能。。。");
return obj;
}
}
切入点表达式起别名(@PointCut)
事务
添加注解事务效果掩饰
service.xml
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--导入mapper文件,可以直接获取service.xml-->
<import resource="applicationContext_mapper.xml"></import>
<context:component-scan base-package="com.bjpowernode.service.impl"></context:component-scan>
<!-- 事务处理-->
<!-- 1.添加事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 事务必须关联数据库处理,所以要配置数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2.添加事务的驱动-->
<tx:annotation-driven transaction-manager="transactionManager" ></tx:annotation-driven>
</beans>
serviceimpl.java
/**
* @author may
* @date 2022/4/15 13:10
*/
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class AccountsServiceImpl implements AccountsService {
// 数据访问层对象
@Autowired
AccountsMapper accountMapper;
@Override
public int save(Accounts accounts) {
int num =0;
num = accountMapper.save(accounts);
System.out.println("增加账户成功!"+num);
// 手动抛出异常
System.out.println(1/0);
return num;
}
}
- 添加事务,要是有异常就算数据已经在操作 也不会添加到数据库中
添加不回滚属性设置
1,在后面添加norollbackforClassname 不然就是norollbackfor=ArithmeticException.class
2.forname 代表异常的名字如果有多个异常可以用{“”,“”}
3.这样就算有异常依然能够将操作的数据添加到数据库
@Transactional 常用参数
@Transactional(propagation = Propagation.REQUIRED,//事务的传播特性
noRollbackForClassName = “ArithmeticException”, //指定发生什么异常不回滚,使用的是异常的名称
noRollbackFor = ArithmeticException.class,//指定发生什么异常不回滚,使用的是异常的类型
rollbackForClassName = “”,//指定发生什么异常必须回滚
rollbackFor = ArithmeticException.class,//指定发生什么异常必须回滚
timeout = -1, //连接超时设置,默认值是-1,表示永不超时
readOnly = false, //默认是false,如果是查询操作,必须设置为true.
isolation = Isolation.DEFAULT//使用数据库自已的隔离级别
)
spring事务的两种处理方式
1)注解式的事务
使用@Transactional注解完成事务控制,此注解可添加到类上,则对类中所有方法执行事务的设定.此注解可添加到方法上,只是对此方法执行事务的处理.
2)声明式事务(必须掌握),在配置文件中添加一次,整个项目遵循事务的设定.
事务的管理级别
1).未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
2).提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
3).可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读,但是innoDB解决了幻读
4).串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
5).使用数据库默认的隔离级别isolation = Isolation.DEFAULT
MySQL:mysql默认的事务处理级别是’REPEATABLE-READ’,也就是可重复读
Oracle:oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。默认系统事务隔离级别是READ COMMITTED,也就是读已提交
为什么添加事务管理器
JDBC: Connection con.commit(); con.rollback();
MyBatis: SqlSession sqlSession.commit(); sqlSession.rollback();
Hibernate: Session session.commit(); session.rollback();
事务管理器用来生成相应技术的连接+执行语句的对象.
如果使用MyBatis框架,必须使用DataSourceTransactionManager类完成处理
spring 事务传播的特性
多个事务之间的合并,互斥等都可以通过设置事务的传播特性来解决.
1. 常用
PROPAGATION_REQUIRED:必被包含事务(增删改必用)
PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不单开事务
PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境
2. 不常用
PROPAGATION_MANDATORY:必须包在事务中,没有事务则抛异常
PROPAGATION_NESTED:嵌套事务




