1.事务的概念及特性
1.1 概念
(1)事务:指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败
ps:感觉就是字面意思,一件事
(2)事务的产生:尤其应用在诸如类似银行的业务上,比如A转账1000元钱给B,当A账户余额减少1000后,突然发生了故障,如服务器断开的情况,致使B的账户余额并未来得及增加1000,这并不是我们期望的结果,故而就有了事务的产生。
如果A账户余额增加的动作,和B账户余额减少的动作被定义为一个事务,那么,这两个动作,要么都成功,要么都失败。比如刚刚提到的,突然网中断未来得及执行第二个动作,那么最终结果是两个动作都不执行
所谓事务,就是属于完成同一事务的操作,要么全部执行,要么都不执行
(3)事务的四大特性——ACID
1.2 Atomic——原子性
业务动作(即事务)不可再分,业务动作对应的多条SQL语句看作一个整体,这几条语句要么都执行成功,要么都不执行,不存在某几句执行某几句不执行的情况
1.3 Consistency——一致性
事务的一致性是我们的最终目的,我们期望事务内相关联的数据始终保持一致性,而这一点,纯靠DBMS并不能完全实现,需要编程人员和JDBC共同实现
1.4 Isolation——隔离性
(1)概念
特指多用户场景下,并发控制的内容
当同时多个用户(JDBC中的Connection)操作同一批数据时,我们希望这些操作是相互隔离的,即互不影响,怎么做呢,只能通过破坏同时性,在极小的时间差内,让各个Connection排队解决(用户之间几乎感觉不到)
(2)不同的隔离级别
也就是说,要追求真正的隔离性,是以牺牲并发性为代价的。
据此,在纠结于隔离性和并发性之间,SQL标准制定了隔离级别
1)可串行化——serializable ,完全的追求隔离性,事务之间必须排队进行,严格保证了数据的正确性,同时,性能最差
2)快照读——snapshot read,消除了幻读,实则还并未在SQL标准中,所以mysql中的可重复读近似于快照读
3)可重复读——repeated read,保证了在一次事务中,只要我还未提交,我看到的某个数据就一直是这个数据,即使同时发生的事务B已经修改了该数据,而我这边是感受不到的
缺点:只能对表中已有数据进行保护,而对新插入进来的数据无法保证其可重复读,故而可能导致幻读(什么是幻读呢,幻读就是好像读到了个假数据)。
比如,事务A内容为将表中所有数据的年龄属性都更改为1,然后从查询返回所有数的查询结果,同时进行的事务B内容为插入一条新数据,当B提交后,A查询返回所有数的结果,其他数据的年龄都是1,而新插入的数据是一条例外数据,其年龄属性并没有更改为1,这就叫幻读
4)读已提交——read commited,能读到已经提交了的数据
缺点:不可重复读,就是说,在同一事务中,我多次读取同一个数据,可能读到的数据是不一样的,是被改动过了的
5)读未提交——read uncommited,追求并发性,性能好。但是我和你两个事务同时发生,即使你那边的数据还未提交,我这边也可以读到,相当于读到了脏数据
小小结:
1.5 durability——持久性
操作一旦提交,是不可逆的,是确确实实操作了的
1.6 ACID关系小结
2.事务的使用
2.1 Workbench客户端直接使用
start transaction; -- 事务的开始
-- 借书,先在book表中更新书籍数量,再在借阅记录表中增加借阅记录
update books set count = count - 1 where bid = 1;
insert into records (rid,bid) values (1,1);
commit; -- 提交,只有执行提交这一句,事务动作才真正将结果持久化在硬盘上,如果不提交,更新的数据仍在内存中
【ps】不止是提交,我们也可以手动回滚操作,回滚是rollback
😅验证一波原子性:
在执行完方框内语句后,我们手动重启MySQL服务器,模拟故障:
结果发现,两个表中的数据都没有被修改!说明事务保护了原子性,网络中断导致第二句未执行,所以,执行过的第一句回退,也并未真正对表修改
😅对比一波原子性存在的意义:
2.2 JDBC中事务的使用
Connection中有一个autocomit属性,默认情况下,该属性为true,意思是自动提交每个sql语句,默认视作一个sql是一个事务
故而,如果我们想要让多个sql视为一个事务,我们需要先关闭autocomit,把它修改为false,最后在执行完事务里的sql后,再手动提交事务(commit())。
🥺先写个工具类,对MysqlDataSource类的对象进行初始设置
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author sunny
* @date 2022/04/11 15:53
**/
public class DBUtil {
private static final DataSource dataSource;
static {
dataSource = new MysqlDataSource();
String url = "jdbc:mysql://localhost:3306/book?useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai";
((MysqlDataSource) dataSource).setUrl(url);
((MysqlDataSource) dataSource).setUser("root");
((MysqlDataSource) dataSource).setPassword("123456");
}
public static Connection connection() throws SQLException {
return dataSource.getConnection();
}
}
💌然后,进行sql语句的编写和提交
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**事务
* @author sunny
* @date 2022/04/11 16:11
**/
public class transaction_test1 {
public static void main(String[] args) throws SQLException {
// 依旧以借书操作为例,先更新书籍数量,再新增一条借阅记录
String sql1 = "update books set count = count - 1 where bid = 1";
String sql2 = "insert into records (rid, bid) values (1, 1)";
try(Connection c = DBUtil.connection()){
// 先将自动提交关闭
c.setAutoCommit(false);
try(PreparedStatement ps = c.prepareStatement(sql1)){
ps.executeUpdate();
}
try(PreparedStatement ps = c.prepareStatement(sql2)){
ps.executeUpdate();
}
// 事务动作结束后,手动提交
c.commit();
}
}
}
关键是下面截图中的两处:
🤨🤨🤨
最后的最后,了解事务,事务的ACID特性都是什么,以及客户端和JDBC两种方式使用事务
版权归原作者 ᝰꫛꪮꪮꫜ* 所有, 如有侵权,请联系我们删除。