学习Spring的第7天
环绕通知 @Around
它很强大,但是很少使用
它之所以强大,是因为,它可以改变目标方法的参数。比如:目标方法的参数值。
比如本来人家是参数是10/2,,结果被你改成20/2
其他的四个通知
@Before
@After
@AfterThrowing
@AfterReturn
都不能改变目标方法的参数。
如果由多个切面类的话,那执行顺序是怎样的呢?![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/2b8cf00335ed0716436c4043231653f5.png)
有两个切面类:
如果是一个切面的话顺序是:
前置通知,后置通知,返回通知。
1、LogUtils
2、VaAspect
那么L首字母靠前。
两个切面类的执行顺序:
1、LogUtils前置
2、VaVaAspect前置
3、VaVaAspect后置
4、VaVaAspect返回
5、LogUtils后置
5、LogUtils返回
之所以LogUtils这个切面类在前面,是因为L在V前面(字母顺序)
保持先进后出的原则。其中后置和返回绑在一块儿。
总结一下:AOP的使用场景。
1、日志
2、权限验证
3、安全检查
4、事务控制
基于XML配置的AOP:
先对比一下以前基于注解的AOP:
1、先将目标类和容器类加入到容器中(加上四大注解之一)
2、告诉Spring哪一个是切面类(加注解:@Aspect)
3、给切面类里面的通知方法,加上合适的注解让不同的通知方法在合适的时间点执行。(@Before,@After,AfterReturning,@AfterThrowing)
4、在配置文件里面,加上aop命名空间。
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.rtl" ></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
基于XML配置的AOP:
把之前加的那些注解给它去掉。
第一步之前是这个:
1、先将目标类和容器类加入到容器中(加上四大注解之一)
现在换成bean标签。
<bean id="myMathCalculator" class="com.rtl.impl.MyMathCalculator"></bean>
<bean id="logUtils" class="com.rtl.utils.LogUtils"></bean>
2、告诉Spring哪一个是切面类(加注解:@Aspect)
换成:
需要aop的名称空间
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
告诉Spring哪一个是切面类:
<aop:config>
<aop:aspect ref="logUtils"></aop:aspect>
</aop:config>
3、给切面类里面的通知方法,加上合适的注解让不同的通知方法在合适的时间点执行。(@Before,@After,AfterReturning,@AfterThrowing)
现在换成:
全部通知方法:
整个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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- <context:component-scan base-package="com.rtl" ></context:component-scan>-->
<!-- <aop:aspectj-autoproxy></aop:aspectj-autoproxy>-->
<bean id="myMathCalculator" class="com.rtl.impl.MyMathCalculator"></bean>
<bean id="logUtils" class="com.rtl.utils.LogUtils"></bean>
<aop:config>
<aop:aspect ref="logUtils">
<aop:pointcut id="mypointcut" expression="execution(public int com.rtl.impl.MyMathCalculator.*(int,int))"/>
<aop:before method="logStart" pointcut-ref="mypointcut"></aop:before>
<aop:after method="logEnd" pointcut-ref="mypointcut"></aop:after>
<aop:after-returning method="logReturn" pointcut-ref="mypointcut" returning="result"></aop:after-returning>
<aop:after-throwing method="logException" pointcut-ref="mypointcut" throwing="exception"></aop:after-throwing>
</aop:aspect>
</aop:config>
</beans>
测试:
实现AOP的有两种方式:
基于配置的和基于注解的。
对比两种:
配置:万能,注解做不到的,配置可以做到。
注解:快速方便。
所有重要的切面类使用配置。
Spring声明式事务:
1、环境搭建:创建好数据库和表
事务:多个表同时操作。
准备好数据库:
在数据库tx里面有三张表,分别是:
表1:account 账户表
表字段:用户名和余额。
表2:book表:
书表。
有isbn,书名,价格
表3:book_stock
书的库存表
哪本书还有多少本。
结账:就是减余额,减库存。
两个操作要么一起成功,要么一起失败
配置2:
写好pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring_aop</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.version>5.2.2.RELEASE</spring.version>
<c3p0.version>0.9.1.2</c3p0.version>
<commons-logging.version>1.1.3</commons-logging.version>
<mysql-connector-java.version>5.1.37</mysql-connector-java.version>
<junit.version>4.10</junit.version>
<aspectjrt.version>1.6.12</aspectjrt.version>
<aspectjweaver.version>1.6.12</aspectjweaver.version>
<cglib.version>2.2</cglib.version>
</properties>
<dependencies>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>${commons-logging.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectjrt.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver.version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
</dependencies>
</project>
pom文件导入了哪些包呢?
版本:
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.version>5.2.2.RELEASE</spring.version>
<c3p0.version>0.9.1.2</c3p0.version>
<commons-logging.version>1.1.3</commons-logging.version>
<mysql-connector-java.version>5.1.37</mysql-connector-java.version>
<junit.version>4.10</junit.version>
<aspectjrt.version>1.6.12</aspectjrt.version>
<aspectjweaver.version>1.6.12</aspectjweaver.version>
<cglib.version>2.2</cglib.version>
2、写几个类和方法模拟结账操作。并给他们加上注解:
第一个类:BookDao
写Spring的配置文件
1、写好基础包的扫描
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<context:component-scan base-package="com.rtl"></context:component-scan>
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="pooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
</beans>
配置数据源和JdbcTemplate:
现在这个JdbcTemplate才不会是null
写好直接和数据库交互的类:BookDao
里面有三个方法:
1、给用户减余额
//给account表里面的某个用户买了书之后,给它的余额减掉对应的书的价格
public void updateAccount(String userName,int price){
String sql = "UPDATE ACCOUNT SET balance = balance - ? WHERE username = ?";
jdbcTemplate.update(sql,price,userName);
}
2、查询对应图书的价格
//传入图书的isbn,获取这本图书的价格:
public int getPrice(String isbn){
String sql = "SELECT price FROM book WHERE isbn = ?";
Integer price = jdbcTemplate.queryForObject(sql, Integer.class, isbn);
return price;
}
3、给图书减库存
//买了这本书之后,需要减这本书对应的库存,为了简单起见,我们默认每次只买一本书。
public void updateStock(String isbn){
String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?";
jdbcTemplate.update(sql,isbn);
}
总的BookDao.java
package com.rtl.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
//给account表里面的某个用户买了书之后,给它的余额减掉对应的书的价格
public void updateAccount(String userName,int price){
String sql = "UPDATE ACCOUNT SET balance = balance - ? WHERE username = ?";
jdbcTemplate.update(sql,price,userName);
}
//传入图书的isbn,获取这本图书的价格:
public int getPrice(String isbn){
String sql = "SELECT price FROM book WHERE isbn = ?";
Integer price = jdbcTemplate.queryForObject(sql, Integer.class, isbn);
return price;
}
//买了这本书之后,需要减这本书对应的库存,为了简单起见,我们默认每次只买一本书。
public void updateStock(String isbn){
String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?";
jdbcTemplate.update(sql,isbn);
}
}
接下来写BookService类:
它只有一个方法就是结账:
只需要知道哪个用户买了哪本书就行了。
第一步:减书的库存。
第二步:查出这本书的价格
第三步:给用户减余额
测试:保证环境打通了。
为了测试方便,先把:
用户表每个人的余额设置成10000
图书表每本书的价格设置成100
库存表每本书的库存是1000
UPDATE ACCOUNT SET balance = 10000;
UPDATE book SET price = 100;
UPDATE book_stock SET stock = 1000;
初始状态各个表的数据:
1、account:
2、book:
3、book_stock:
之前使用的pom里面的数据库的驱动是低版本
现在改成:
因为我的mysql的版本是:
现在测试成功:
查看一下数据的数据:
1、Tom用户的余额会减掉100元
2、isbn=1的图书会减掉一本。
到这里我们的环境就已经搭建成功了。
现在开始正式讲:声明式事务:
声明式事务 VS 编程式事务
编程式事务:
这种事务控制的不精准。这只是事务控制的思想。
声明式事务:
只要声明哪个方法是事务方法就可以了,Spring会自动进行事务控制。
以前是通过复杂的编程来编写事务。
我们需要告诉Spring。哪个方法是事务方法。
之前学的AOP:
所有事务控制的模式都是一个套路的:
1、获取连接
2、设置非自动提交
3、目标代码的执行
4、正常情况的提交
5、异常情况的回滚
6、最终关闭
AOP的环绕通知就可以去做。
事务:
要么都成功执行。
要么都不成功
ACID
原子性:减库存和减余额的两种操作。是一个原子,不可再分。
一致性:我有1000元,他有1000元,两个用户再怎么互相转钱,总共只有2000元。
隔离性:数据库的并发隔离能力
持久性:数据库服务器断电重启之后,还是原来的数据。
Spring的声明式事务:
事务管理的代码是固定的。
z
这些固定的代码。可以作为一种横切关注点。可以通过AOP方法模块化。所以,我们可以借助Spring AOP框架进行事务管理。
但是我我们要是自己写切面类,又会比较麻烦。
所以,Spring已经帮我们写好了这个切面类–专业术语叫做事务管理器。
搜索:PlatformTransactionManager
Spring会根据你使用的持久层的框架不一样,而选择不同的事务管理器。
使用原生JDBC操作数据库的,那就使用:
DataSourceTransactionManager
DataSourceTransactionManager这个事务管理器,就可以在目标方法运行前后进行事务控制。
DataSourceTransactionManager这个也叫做事务切面。
相当于之前的LogUtils
快速的为某个目标方法添加事务:
我们目前使用DataSourceTransactionManager这个即可。
所有要能工作的组件必须要在IOC容器里面。
1、配置这个管理器DataSourceTransactionManager,让他进行事务控制。很简单,就是加bean标签。
事务管理器要能控制住事务,其实:能控制住连接就行了。
因为之前的操作都是使用连接:
比如:
拿连接设置自动提交
拿连接做回滚
…
所以,这个DataSourceTransactionManager也一样,它要想控制事务,只要控制住connection就行了。
而我们jdbc操作数据库,都是通过数据源获取连接。
所以,我们拿DataSourceTransactionManager事务控制器,把数据源控制住就好了。
所以,最终配置事务管理器,让他进行事务控制,很简单:就是这样:
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.rtl"></context:component-scan>
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="pooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
</beans>
第二步:开启基于注解的事务控制模式,这里要依赖tx名称空间。
所有的配置文件:
<?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
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.rtl"></context:component-scan>
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="pooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
实际上这里使用事务控制器,其实就是AOP的原理,所以需要导入AOP的那几个东西。
第三步:
给事务方法加上注解就可以。
我们的BookService类里面的结账方法就是事务方法。
加上注解:
@Transactional
测试正常功能:
数据库的数据变了:
配置文件的内容:
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.rtl"></context:component-scan>
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="pooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
我们再测试一下异常情况:
1、首先把@Transactional给注销掉。
我们来测试一下没有事务管理的时候。操作数据库:
但是我们要故意写错sql:
因为我们的方法里面是先修改库存,再改余额。
所以,我们去改最后一个方法,也就是故意写错该用户余额的sql。这样第一个sql,改库存就能执行成功了(因为没有事务)。
改库存是正确的sql:
把减用户的余额的sql故意写错:
改用户余额,原先正确的sql、。
现在错误的sql:
测试异常(没有加注解@Transactional 也就是没有事务管理的)
会出现:图书ISBN-001库存书数量-1,但是用户Tom的余额没有发生改变。
所以没有加事务管理会出现:
先执行修改图书库存成功了。
但是后面执行减少用户余额的sql失败。
这就没有事务(ACID)
测试正确的(加上了事务管理的注解):
结论:
只要有一个sql发生了错误,那么数据库中的数据,谁也不会变。
(要失败,都失败。要成功都成功)原子性。
事务的细节1:timeout 是int类型
超时,事务超出指定执行时长后自动终止并回滚。
timeout = ? s(注意单位是秒)
也就是这个checkout()方法要在三秒之内执行完,否则也是失败,三个方法全部不成功。
事务细节2:readOnly 是boolean类型
它是对事务进行优化的。
readOnly 适合什么时候使用呢?
当你操作数据库的方法全部都是读取的时候。
可以设置readOnly = true
默认值是false
如果只是对数据库进行查询,那设置readOnly=true,可以加快查询数据库的速度。
但是如果你是有增删改操作的,那就不能写readOnly = true
否则直接报错。
PreparedStatementCallback; SQL [UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?]; Connection is read-only. Queries leading to data modification are not allowed;
Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1070)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1046)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1371)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1031)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
事务细节3:
指定哪些异常出现的时候,不需要回滚。
异常的分类:
运行时异常:可以不用处理
编译时异常:要么try catch,要么再方法签名上进行声明。
现在就是:
编译时异常不回滚。
运行时异常回滚。
事务细节三:noRollbackFor Class[]
指定哪些异常可以不回滚。
原来默认,运行时异常都会回滚,。
比如:做除法操作的时候。如果除数是0,那么会发生运行时异常。默认是会回滚的,现在想让这个不回滚。
因为它的数据类型是数据的
使用{}来表示。逗号分隔。
@Transactional(noRollbackFor = {ArithmeticException.class})
指定多个异常类:
事务细节3:rollbackFor
这个和之前的noRollbackFor 刚好相反。
他是指定原本不会回滚的异常,现在也要回滚。
比如:编译时异常默认全部都不回滚。
FileNotFoundException
这个异常就是编译时异常,如果我们啥也不设置,那么发生这个编译时异常的时候,是会选择回滚的。
现在我们设置:
这个时候,即使发生异常,也是不会进行回滚的。
事务细节4:isolation Isolation数据类型,隔离级别
问:什么叫做隔离级别呢?
三种数据库事务并发的问题:
1、脏读:(一定不允许发生)
1、事务1:修改一条记录的数据由20到30,但是没有提交;
2、事务2:读取了事务1修改但是未提交的数据30
3、事务1后悔修改,回滚了,数据还是变回了20
4、结论:事务2读取到的数据30就是脏数据,这就是脏读。
2、不可重复读;(允许发生)
1、事务1读取数据是20
2、事务2修改事务1刚才读取的数据,把20改为30,并且提交了。
3、事务1过来又重新都刚才读过的数据,发现值为30,和上次读的不一样了。
3、幻读
1、事务1读工资大于4000的员工,发现有三条记录
2、事务2插入8条数据,其中有两条数据是工资大于4000的。且已经提交。
3、事务1再去读工资大于4000的数据。发现有5条记录。这个叫做幻读,可能多读了,或者少读了数据。
总结:
三种数据库并发引起的三种问题里面,只有脏读是坚决不允许发生的。
数据库为了应对数据库并发导致的这三种问题的出现,然后就有隔离级别了。
数据库针对三种并发问题出现的三种隔离级别:
1、读未提交:read uncommited 禁止设置数据库的隔离级别为这个,因为会出现脏读现象。
可以读出 人家没有提交的数据。这种情况会出现脏读,把人家没有提交的(修改数据的操作没有提交)数据给读出来了。
2、读已提交 read commited 可以设置,但是会出现不可重复读和幻读的情况。因为第一次读和第二次读,中间执行了增删改的操作且这个操作已经提交,导致第二次读出来的数据和第一次读出来的数据不一致,但是这个允许出现。因为毕竟读出来的是别人已经提交的数据。
只能 读出人家已经提交的数据。