学习Spring的第7天

学习Spring的第7天

在这里插入图片描述

环绕通知 @Around

它很强大,但是很少使用
它之所以强大,是因为,它可以改变目标方法的参数。比如:目标方法的参数值。
比如本来人家是参数是10/2,,结果被你改成20/2
其他的四个通知
@Before
@After
@AfterThrowing
@AfterReturn
都不能改变目标方法的参数。

如果由多个切面类的话,那执行顺序是怎样的呢?在这里插入图片描述

有两个切面类:
如果是一个切面的话顺序是:
前置通知,后置通知,返回通知。
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 可以设置,但是会出现不可重复读和幻读的情况。因为第一次读和第二次读,中间执行了增删改的操作且这个操作已经提交,导致第二次读出来的数据和第一次读出来的数据不一致,但是这个允许出现。因为毕竟读出来的是别人已经提交的数据。

只能 读出人家已经提交的数据。

3、可重复读。repeated read

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值