目录

42-事务问题演示

文档阅读

  • 一个业务逻辑需要操作多次数据库。
  • 买书的例子:
    • 图书数据库,查询书的价格。
    • 库存表中,减少库存。
    • 用户的余额中,减少金钱。
  • 数据库操作:要么都执行,要么都不执行。
    • 原子性:事务数据库操作,缺一不可。
    • 一致性:事务数据库操作前后,业务数据逻辑正确。
    • 隔离性:事务往往并发执行,多个事务处理相同数据,需要隔离。
    • 持久性:数据库修改永久保存,持久化存储器=硬盘。
  • 编程式事务管理:原生JDBC API实现事务管理,是基石。
    1. 获取Connection连接对象。
    2. setAutoCommit设置false,取消自动提交。
    3. 执行操作。
    4. 手动提交事务。
    5. 执行失败时回滚。
    6. 释放资源。

编程式事务管理,代码在业务方法中,service层,多个模块使用事务管理,会代码冗余。

  • 声明式事务管理:事务管理是AOP经典体现。

模拟Servlet-Service-Dao

day04-Spring事务操作详解_数据

数据库创建表

day04-Spring事务操作详解_杨博超-SSM_02

day04-Spring事务操作详解_数据_03

day04-Spring事务操作详解_spring_04

day04-Spring事务操作详解_xml_05

从Dao往Controller写代码

  • 查价格
  • 减库存
  • 减余额

day04-Spring事务操作详解_xml_06

package com.atguigu.book.DaoImpl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.atguigu.book.Dao.BookDao;

@Repository
public class BookDaoImpl implements BookDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Override
	public Integer selectPrice(String bid) {
		Integer price = jdbcTemplate.queryForObject("select price from book where bid = ?", new Object[] {bid}, Integer.class);
		return price;
	}

	@Override
	public void updateSt(String bid) {
		//判断有没有库存,库存为0,不操作
		Integer st = jdbcTemplate.queryForObject("select st from stock where sid = ?", new Object[] {bid}, Integer.class);
		if (st <= 0) {
			throw new RuntimeException();
		}else {
			jdbcTemplate.update("update stock set st = st - 1 where sid = ? ", bid);
		}
	}

	@Override
	public void updateBalance(String uid, Integer price) {
		//查询余额够不够
		Integer balance = jdbcTemplate.queryForObject("select balance from money where uid = ?", new Object[] {uid}, Integer.class);
		if(balance < price) {
			throw new RuntimeException();
		}else {
			jdbcTemplate.update("update money set balance = balance - ? where uid = ?", price, uid);
		}
	}
}

service层

package com.atguigu.book.ServiceImpl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.atguigu.book.Dao.BookDao;
import com.atguigu.book.Service.BookService;

@Service
public class BookServiceImpl implements BookService {

	@Autowired
	private BookDao dao;

	@Override
	public void buyBook(String bid, String uid) {
		Integer price = dao.selectPrice(bid);
		dao.updateSt(bid);
		dao.updateBalance(uid, price);
	}	
}

controller层

package com.atguigu.book.Controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import com.atguigu.book.Service.BookService;

@Controller
public class BookController {

	@Autowired
	private BookService service;
	
	public void buyBook() {
		String bid = "1";
		String uid = "1001";
		service.buyBook(bid, uid);
	}
}

test

package com.atguigu.book;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.atguigu.book.Controller.BookController;

public class Test {

	public static void main(String[] args) {
		
		ApplicationContext ac = new ClassPathXmlApplicationContext("book.xml");
		BookController controller = ac.getBean("bookController", BookController.class);
		controller.buyBook();
	}
}

小问题

day04-Spring事务操作详解_xml_07

day04-Spring事务操作详解_事务管理_08

2019年6月14日 - 结束

day04-Spring事务操作详解_xml_09

2019年6月15日 - 开始

day04-Spring事务操作详解_xml_10

43-回顾
  • AOP术语

    • OOP是纵向继承机制,具有非业务代码加入业务代码情况,如日志、事务。
    • AOP是公共功能、非核心代码,抽取。
    • 目标对象的公共代码,叫做横切关注点
    • 抽取代码,放到一个类中,这个类叫切面
    • 类中,这些公共代码,叫做通知
    • 通知作用域目标对象的位置,叫做连接点
    • 通知作用到目标对象的方式、条件,叫做切入点
  • 总结AOP术语

    • 目标类:含有多个方法。
      • 多个方法的多个公共代码(横切关注点
      • 每个方法中不同公共代码的位置(连接点
    • AOP动态代理类:不可见,自动生成。
    • 切面类
      • 多个公共代码(通知
      • 通知对某个类某个方法的定位方式(切入点
  • 通知

    • 前置通知
    • 后置通知
    • 异常通知
    • 返回通知
    • 环绕通知
  • AspectJ:AOP是一种思想,AspectJ是一种实现

    • cglib的包
    • aopalliance-aspectj的依赖jar包
    • weaver-aspectj的核心jar包
    • <context:component-scan>
    • <aop:aspectj-autoproxy>
    • 注解
      • @Component
      • @Aspect
        • @Before(value="切入点表达式")
        • @After(value="切入点表达式")
        • @AfterReturning(value="切入点表达式", returning="result")
        • @AfterThrowing(value="切入点表达式",throwing="ex")
        • @Around(value="切入点表达式")、ProceedingJoinPoint
        • @Pointcut(value="切入点表达式")
      • @Order
  • spring原生aop:

    • aop命名空间
    • <aop:config>
    • <aop:aspect>
    • <aop:pointcut>
    • <aop:before>: method, pointcut-ref, pointcut
  • JdbcTemplate

    • 资源文件
    • 数据源
    • JdbcTemplate:dataSource。
    • 四个方法
      • update:单条数据增删改
      • batchUpdate:批量增删改
      • queryForObject:查询单条数据或单个值
        • rowMapper接口,beanPropertyRowMapper
      • query:查询list集合

API的问题

z-tree,树形菜单

day04-Spring事务操作详解_事务管理_11

day04-Spring事务操作详解_spring_12

day04-Spring事务操作详解_事务管理_13

上面是ztree的demo效果,以后做权限管理,可能会做到。

权限管理,最少用五张吧,用户表,角色表,权限表,用户对角色,角色对权限。

权限管理,SQL要五表联查。左外链接、右外连接。

ztree也要学习下。

有的项目,完全不要样式,只要功能。

UI工程师。

也有适合后台程序员使用的前台框架,bootstrap(封装css),jquery easyUI(封装js)

写代码,前台成就感,比后台成就感高。

公司UI工程师很少。

后台管理时候,不要求页面长什么样。自己去写,要用前端框架。

44-注解配置事务

DataSourceTransactionManager - 数据源事务管理器

day04-Spring事务操作详解_spring_14

day04-Spring事务操作详解_数据_15

AbstractPlatformTransactionManager

spring抽象出来的,事务最基本的功能,放在,抽象类AbstractPlatformTransactionManager中。

根据事务使用的技术不同,抽象类,又具有各种子类。

day04-Spring事务操作详解_xml_16

  • DataSourceTransactionManager - Jdbc
  • HibernateTransactionManager - Hibernate
  • JtaTransactionManager - Jta也是一个持久层框架

使用哪一种技术,我们就用哪一个类。


配置DataSourceTransactionManager

day04-Spring事务操作详解_杨博超-SSM_17

因为我们讲的是,注解式的事务管理,是依赖于aspectj的jar,最主要依赖下图三个jar包中,中间的那个jar包。

day04-Spring事务操作详解_杨博超-SSM_18

修改代码

day04-Spring事务操作详解_spring_19

只此一步,就完了,测试成功。

事务,特别简单。

  • xml配置文件中,配置一个事务管理器对象,开启注解驱动。
  • 在service业务逻辑层,添加@Transactional注解。
45-@Transaction的属性
  • @Transaction可以加在方法上,也可以加在类上。

day04-Spring事务操作详解_数据_20

注解的属性

有一种情况,方法上注解加了一个属性,类上注解加了一个属性,这个属性应该以谁为准呢?

采用就近原则,应该采用类上的属性。

我们要讲解@Transactional中的每个属性。

day04-Spring事务操作详解_杨博超-SSM_21

46-事务的传播行为演示

闲聊

  • 第一份工作,软件公司,实施顾问。
  • 第二份工作,卖山药,淮山药,劲大。男的吃多了女的受不了,女的吃多了男的受不了,两人都吃多了床受不了。
    • 在家玩耍,晚上上网,白天睡觉,刷信用卡。
    • 公交车回家,听说燃气公司福利待遇好。
  • 第三份工作,燃气公司面试,售后服务。
    • 政务服务大厅,办燃气,修电脑,待了2个月。
    • 抄水表部门。很轻松。1200块一个月,一年10%。

演示

day04-Spring事务操作详解_spring_22

day04-Spring事务操作详解_xml_23

一次买两本书,余额就够买一般书,第一本书买成功,第二本书没买成功。

业务逻辑:要么都能买成功,要么都不能买成功。

添加事务管理:

day04-Spring事务操作详解_杨博超-SSM_24

测试效果正常。

47-事务的传播行为

propagation - 传播

day04-Spring事务操作详解_spring_25

day04-Spring事务操作详解_spring_26

如上图,checkOut的事务,传递给了buyBook,buyBook是依赖于本身的事务,还是依赖调用者的事务呢?

buyBook可以选择用自己的事务,也可以使用调用者的事务。

通过propagation属性进行设置。

day04-Spring事务操作详解_xml_27

  • REQUIRED(默认值):调用方有,就用调用方,调用方没有,必须自己干

    • 调用方有事务,用调用方的事务。
    • 调用方没有事务,自己启动新事务运行。
  • REQUIRED_NEW必须自己干

    • 调用方有事务,自己启动新事务运行。
    • 调用方没有事务,自己启动新事务运行。
  • SUPPORTS调用方有,就用调用方,调用方没有,不用自己干

    • 调用方有事务,用调用方的事务。
    • 调用方没有事务,不用事务。
  • NOT_SUPPORTS不能有事务,有不报错

    • 调用方有事务,不用事务。
    • 调用方没有事务,不用事务。
  • MABDATORY: 必须用调用方

    • 调用方有事务,用调用方的事务。
    • 调用方没有事务,抛出异常。
  • NEVER不能有事务,有就报错

    • 调用方有事务,抛出异常。
    • 调用方没有事务,不抛异常。
  • NESTED调用方有,用调用方的嵌套,调用方没有,必须自己干

    • 调用方有事务,用调用方的嵌套事务。
    • 调用方没有事务,自己启动新事务运行。

总结

day04-Spring事务操作详解_杨博超-SSM_28

代码

day04-Spring事务操作详解_事务管理_29

48-事务的隔离级别

isolation - 隔离

事务的隔离级别,一般用在并发中,多个请求操作数据库,应该以什么方式并发操作呢?

如果在事务@Transaction中不设置,默认是和数据库的隔离级别保持一致。

mysql的隔离级别是可重复读。

  • 读未提交:【脏读】、针对表中字段、对应数据1。
    • A操作数据库的一张表X,修改年龄字段,20修改为30,A还没有提交事务。
    • B读同一张表X,读取年龄字段,可以读取到30这个数据(未提交事务数据)。
    • 这时候,A回滚了事务,B读取的30这个数据,没有业务意义。
    • 读未提交,造成的问题,【脏读:读取没有意义数据】。
  • 读已提交:【不可重复读】、针对表中字段、对应数据2。
    • A操作数据库的一张表X,修改年龄字段,20修改为30,A还没有提交事务。
    • B读同一张表X,读取年龄字段,只能读取到20这个数据(已提交事务数据)。
    • 这时候,A提交了事务,数据变成了30。
    • B第二次读取这张表X,读取年龄字段,拿到的是30这个数据,读取的两条数据不一致。
    • 读已提交造成的问题是,【不可重复读:两次读的数据不一致】。
  • 可重复读:【幻读】、针对表中记录、对应数据4。
    • B读取数据库的一张表X,表X当中存在了Y条数据,B读取这些Y数据,A无法修改,B可以重复次数读取
    • A不修改数据,A添加数据,A添加了10条数据。
    • B读记录之前,查询过只有Y条数据,读着读着读出来Y+10条数据。
    • 可重复读造成的问题是,幻读。
  • 串行化:【单线程、性能太低、消耗太大】、对应数据8。
    • A对数据库请求、B对数据请求。
    • B操作数据的时候,A不能增删改查。

linux的权限,三个,读,写,执行。

读是1,写是2,执行是4,加起来是7。

给我一个7的权限,读写执行全部都有。

day04-Spring事务操作详解_数据_30

49-事务的超时操作

timeout

在事务强制回滚前,最多可以执行(等待)的时间。

什么叫事务的强制回滚。

举例双十一秒杀,秒杀时候并发量特别大。

实现功能前,要测试,模拟10000并发,测试出来数据库处理每个请求花费的时间,根据这个时间设置超时时间。

程序执行中,线程调度出现问题,有的请求卡在执行过程中,10000个请求都卡住,不能一直等着。

day04-Spring事务操作详解_数据_31

mysql默认同时支持100个连接同时访问。

mysql性能没有那么强大,我们不能同时让这些请求卡住。

我们需要根据mysql处理请求耗时,设置超时时间,强制回滚超时的请求。

这就是强制回滚。

所以需要设置事务的超时时间。

day04-Spring事务操作详解_xml_32

测试一下:

day04-Spring事务操作详解_杨博超-SSM_33

day04-Spring事务操作详解_xml_34

50-事务的只读

readOnly

html当中,表单元素有readonly属性,表示不能修改,只能读,不能修改。

演示html当中input的readonly属性

day04-Spring事务操作详解_spring_35

day04-Spring事务操作详解_数据_36

day04-Spring事务操作详解_spring_37

day04-Spring事务操作详解_数据_38

day04-Spring事务操作详解_spring_39

day04-Spring事务操作详解_数据_40

day04-Spring事务操作详解_事务管理_41

在这里我出现一个问题,启动tomcat,访问8080无法出现tomcat页面。

我做了如下设置:

day04-Spring事务操作详解_事务管理_42

解决了问题。

这是参考了CSDN的这篇文章

就是下面的这个效果:

day04-Spring事务操作详解_spring_43

readonly是只读,服务器能够读取,但是页面不能修改。

day04-Spring事务操作详解_spring_44

这里disabled之后,服务器获取不到input的值。


事务的readonly

当前事务中,一系列的操作,是否为只读。

这个事务中,全部都是读的操作。

这个事务中,有读的操作,也有写的操作。

readonly默认为false。

如果你的事务当中,全部都是读的操作,你就可以设置为readonly。

这样spring就会通知mysql。

mysql当中有锁的操作。

锁有悲观锁和乐观锁两种。

mysql实现过程中,用得的多线程和锁。

用户操作数据,mysql会对数据加锁,其他请求不能进行任何操作,读都不行。

用户操作完成后,锁就会释放掉,别的请求就可以访问了。

下一个请求访问数据的时候,mysql还是一样会上锁。

加锁就是为了防止,脏读,不可重复读,幻读。

只读情况下,spring会告诉mysql,都是只读数据,不对数据进行操作,不要加锁了。

事务当中设置为只读之后,mysql就在请求访问数据的时候,不加锁,提高性能。

还有一种情况,我的事务中,不仅仅有读的功能,还有写的功能。你也可以加上readonly为true,这个时候就危险了。

所以,你不要乱加这个属性。

51-事务回滚的条件

rollbackfor 和 rollbackForClassName

day04-Spring事务操作详解_事务管理_45

rollbackfor属性默认是个大括号,可以在这里写异常情况,比如我们写nullPointException,那么只有空指针时候才会回滚。

演示

创建自定义异常类

day04-Spring事务操作详解_spring_46

day04-Spring事务操作详解_事务管理_47

day04-Spring事务操作详解_spring_48

noRollbackFor 和 noRollbackForClassName

day04-Spring事务操作详解_事务管理_49

2019年6月15日 - 下午

day04-Spring事务操作详解_xml_10

52-事务回顾
  • @Transactional
    • propagation
    • isolation
    • timeout
    • readonly
    • rollbackfor 、rollbackForClassName
    • noRollbackFor、noRollbackForClassName
53-xml方式的事务配置

这部分只是了解

我们先回顾一下,通过xml配置spring切面的知识,如下图所示:

day04-Spring事务操作详解_杨博超-SSM_51

而事实上,事务管理器,就是一个切面,目标对象是service实现类,需要作用域service实现类中的方法。

我们现在有了切面类,我们还需要有切入点表达式。

如下面的图,所示,我们在xml当中,使用aop-xml配置的方式,配置切面类的,切入点表达式,如下所示:

day04-Spring事务操作详解_xml_52

day04-Spring事务操作详解_杨博超-SSM_53

我们总结一下:

1、事务管理器,是一个切面。

2、配置切面的通知,切面的通知,要跟事务管理器关联起来。

3、通知要有切入点表达式。切入点表达式要跟通知关联起来,使用<aop:advisor></aop:advisor>

整体的代码如下:

<?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"
	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.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
	
	<!-- 配置注解,自动包扫描 -->
	<context:component-scan base-package="com.atguigu.book_xml"></context:component-scan>
	
	<!-- 引入数据资源文件 -->
	<context:property-placeholder location="db.properties"/>
	
	<!-- 根据资源文件,配置数据源 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="driverClassName" value="${jdbc.driver}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- 根据数据源,配置jdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置事务管理器,这是一个切面 -->
	<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置切面的通知 -->
	<tx:advice id="tx" transaction-manager="dataSourceTransactionManager">
		<tx:attributes>
			<!-- 在设置好的切入点表达式下,对事务进行再次的设置,定位类中的方法 -->
			<tx:method name="buyBook" />
			<tx:method name="checkOut" />
		</tx:attributes>
	</tx:advice>
	
	<!-- 配置切入点表达式,这里是定位包、定位类-->
	<aop:config>
		<aop:pointcut id="pointCut" expression="execution(* com.atguigu.book_xml.ServiceImpl.*.*(..))"/>
		<aop:advisor advice-ref="tx" pointcut-ref="pointCut"/>
	</aop:config>

</beans>

day04-Spring事务操作详解_xml_54

一些补充

day04-Spring事务操作详解_事务管理_55

2019年6月15日 - 下午 - 15:00结束
乖乖学习,好好做事。