spring04:事务控制、定时器



第三部分:事务控制


一、场景

如果不进行事务控制,在进行批量插入数据时,如果部分数据有错误,插入过程中,可能会把部分正确的数据插入到数据库。这样做出现的问题就是可能会破坏数据的完整性。
我们想要当插入的数据都正确时,我们才会全部插入,如果数据中有错误的话,就全部回滚,都不插入。即使数据有错误,也不会对数据库造成影响。

1. 测试

模拟一下如果不做事务控制,可能出现的问题。
开始时数据中的数据是这样的:
在这里插入图片描述
如果不做事务控制,直接插入数据:

StudentService

// 模拟批量插入
    int insertBatch();

StudentServiceImpl

向输入库中插入id为9-11的学生信息

	@Override
    public int insertBatch() {
        int count = 0;
        for (int i=9; i<12; i++) {
            Student student = new Student();
            student.setId(i);
            this.studentMapper.insertSelective(student);
            count++;
        }
        return count;
    }

StudentTest

	@Test
    public void insertBatch() {
    	// 获取bean组件
        StudentService studentService = (StudentService) ac.getBean("studentServiceImpl");
        // 插入数据
        studentService.insertBatch();
    }

批量添加数据后,会报异常主键11冲突,但数据库的数据是这样的:
在这里插入图片描述
说明虽然有部分数据是错误的,但是仍可能会插入部分正确的数据。这样就会对数据库造成影响,破坏数据的完整性。

二、事务概述

事务控制是通过AOP实现的,通过前置通知、后置通知将一组操作结合在一起构成一个事务,通过异常通知,来检测事务执行过程中的错误。

1. 什么是数据的事务

事务是一组操作的执行单元。相对于数据库单条SQL语句的操作,事务管理是将一组SQL指令看做一个整体。

2. 事务的四大特性

  1. 原子性(atomic):事务里的操作,要么都执行,要么都不执行
  2. 一致性(consistent):如果事务有错误,数据库中原有的数据不会被破坏
  3. 隔离性(isolate):事务和事务之间不会相互混淆,有隔离级别
  4. 持久性(durable):事务一旦执行成功,就会将数据永久的保存到数据库或硬盘中

3. spring提供的两种事务管理

3.1 编程事务管理

程序员自己写程序来控制事务的开始和结束。优点是可以将事务管理的更精细。缺点在于不利于团队开发,会引起事务管理混乱。

3.2 声明事务管理(常用)

使用spring事务管理器,调用的是第三方组件来完成事务控制。我们需要在spring配置文件中做一些配置,就可以将数据库的访问纳入到事务管理中,解除了和代码的耦合,这是对应用程序影响最小的选择。当不需要事务管理时,直接从配置文件中移出该配置即可。

事务管理器的实现:
org.springframework.jdbc.datasource.DataSourceTransactionManager
在单一的JDBC Datasource中的管理事务

三、事务控制相关概念

一般是在service层设置事务控制的。这里需要理解三个概念,传播策略、隔离级别、只读事务。

1. 传播策略

传播策略研究的是怎么合并事务的问题。
在这里插入图片描述
这里重点介绍 REQUIRED
该策略的作用是在调用其他方法时,会取消掉其它方法的事务,只使用本方法的事务。这样的话,如果事务中出现错误,就全部回滚。

2. 隔离级别

一般选取的是 DEFAULT
在这里插入图片描述
通过设置不同的隔离级别可能引发的问题有以下三种:

2.1 脏读

脏读就是一个事务读到了另一个事务修改但并未提交的数据。可能引发的后果是,如果该数据被回滚,则读到的数据是无效的。

2.2 幻读

幻读是一个事务读取到了另一个事务新增的记录,导致读到的记录数不同。

2.3 不可重复读

不可重复读是一个事务多次读同一个数据,但每次读到的内容不同。

3. 只读与读写

只读和读写与数据库或数据库驱动程序相关,并不是一个强制选项。如果一个事务声明为只读,那么数据库或驱动程序就会对这个事务进行一定的优化。比如说不安排相应的数据库锁,不记录回滚日志等,可以减轻事务对数据库的压力,毕竟事务也是需要消耗数据库资源的。

3.1 使用场景

只读事务:单纯的数据库查询
读写事务:对数据库进行修改的操作

四、事务控制的实现

1. 引入事务控制的标签

在这里插入图片描述

2. 配置事务管理器

	<!--配置事务管理器-->
	<!--将事务管理器转为spring容器的组件-->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!--将访问这个数据源的数据库纳入事务控制-->
		<property name="dataSource" ref="dataSource"></property>
	</bean>

3. 启动事务控制的注解驱动

使用注解来实现管理事务的话,需要配置该驱动:

<!--启动事务控制的注解驱动-->
	<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

4. 使用注解完成事务控制

含义/作用推荐
@Transactional事务控制的注解
propagation设置传播策略REQUIRED
isolation设置隔离级别数据库默认级别
readOnly设置读写如果只是查询函数,使用只读,效率更高

StudentServiceImpl

service的实现类里设置事务。

	@Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
    public int insertBatch() {
        int count = 0;
        for (int i=9; i<12; i++) {
            Student student = new Student();
            student.setId(i);
            this.studentMapper.insertSelective(student);
            count++;
        }
        return count;
    }

这样,如果插入的数据存在主键冲突。就不会将任何一条数据插入到数据库中,而是全部回滚。


第四部分:定时器


一、定时任务

按照指定的时间周期运行指定的任务。

二、定时器配置

1. 引入定时器的标签

引入命名空间和xsd文件
在这里插入图片描述

2. 引入定时任务管理器,启动注解驱动

	<task:scheduler id="qbScheduler" pool-size="5" />
	<!--启动注解驱动,设置定时任务的线程池为5-->
	<task:annotation-driven scheduler="qbScheduler" mode="proxy"/>

3. MyTask.java

模拟定时器任务,spring框架启动后,每1s执行一次任务

package com.tentact.scheduler;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class MyTask {

    @Scheduled(fixedRate = 1000)
    public void showMsg() {
        System.out.println("好好学习,天天向上");
    }
}

4. 测试

启动spring框架,用来测试定时器任务

	@Test
    public void testTask() {
        try {
        	// 休眠500000s
            Thread.sleep(500000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

三、cron时间表达式

功能比较强大,可用来指定具体的时间和周期。
一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素
cron时间表达式设置时间的次序为:秒 分 时 日 月 周几 年,注意这里日和周几是互斥的,只能设置一个。

1. 相关符号

符号含义
?来表示不设置
,表示并集,并列的时间
0/x每隔x的时间触发
*表示任意

2. 相关示例

表达式含义
“0 0 10,14,16 * * ?”每天上午10点,下午2点,4点
“0 0/30 9-17 * * ?”朝九晚五工作时间内每半小时
“0 0 12 ? * WED”表示每个星期三中午12点
“0 0 12 * * ?”每天中午12点触发
“0 15 10 ? * *”每天上午10:15触发
“0 15 10 * * ?”每天上午10:15触发
“0 15 10 * * ? 2005”2005年的每天上午10:15触发
0 0/5 14 * * ?”在每天下午2点到下午2:55期间的每5分钟触发

3. 相关代码

MyTask.java

package com.tentact.scheduler;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class MyTask {
	// 每一秒执行一次
    @Scheduled(cron = "0/1 * * * * ?")
    public void showMsg() {
        System.out.println("好好学习,天天向上");
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员_动次动次

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值