知识分享
JDBC事务

目录:


事务的4个条件ACID


  • 原子性( Atomicity):事务可以保证一组操作,要么全部成功;要么全部滚。数据库中不会出现持久化的中间态数据。如A给B转账50元,要么转账成功A - 50, B + 50,要么转账失败A, B账户金额不变。不会出现持久的只有 A - 50 但 B 没有 + 50 的情况。

  • 一致性 (Consistency):事务执行后,数据库状态与其他业务规则保持一致。如转账业务,无论事务执行成功否,参与转账的两个账号余额之和应该是不变的。一致性与原子性息息相关,是一件事物的不同观察角度。

  • 隔离性(Isolation):所谓隔离性是指事务与事务并发时的隔离,即在一个事务提交完成前,其他事务是否能读取甚至改写此事务修改了的数据,具体可通过设置隔离级别来控制,参见事务隔离级别。

  • 持久性(Durability):事务提交后,如果软、硬件崩溃也要保证数据的正确。这是靠先写日志后操作数据的方法,即事务提交时日志是完好的,如果崩溃也可以通过日志来恢复数据。这也说明可靠性和高速度不可兼得, 写日志必然要损失性能。可以想象一下,正在交易的股票市场突然发生了战争(或自然灾害)服务器壮烈牺牲了。当战争过去后人们发现系统盘已经损坏启动不了了,数据库装在系统盘上也不能运行了,但是数据盘侥幸逃过一死(或备份的数据还在),通过日志人们可以在一个新安装的数据库上完整的还原出所有的事务将数据库带到最终的状态从而还原出数据。

并发带来的问题


1、脏读:当前事务读取另外一个事务尚未提交的数据。

image.png

2、不可重复读:在当前事务过程中首次读取数据为A,当再次读取这个数据前由于其它并发事务的提交(注意一定是其它事务提交了造成的修改,如果未提交的修改读取到了就是脏读了)修改了这个数据,因次再次读取就会读到数据为B。(再次强调:1.不可重读针对并发事务已经提交的数据。2.两次或多次读取同一条数据。) 


image.png

3、幻读:在当前事务过程中首次查取年龄大于60的人的记录有5条记录,再次查取前另一个并发事务提交插入了2条记录,因此再次查取得到了7条数据与前一次查询不一致。(注意:1.幻读针对并发事务已经提交的数据。2.两次或多次读取不同行数据,数量上新增或减少。) 


image.png

事务隔离级别


1. Read Uncommitted:没有隔离

2. Read Committed:解决了脏读问题

并发事务提交的数据可以立刻读到,不需要对查询进行过多的限制,性能高。

3. Repeatable Read(MySQL默认隔离级别):解决了脏读和不可重复读问题

简单的处理方法为,将当前事务读取的数据(修改的数据肯定要加锁,毋庸置疑)加锁,这样并发事务遇到锁后挂起等待,这样就可以解决不可重复读问题。

MySQL通过(MVCC)即多版本并发控制,简单的说就是在数据行上加版本号,当事务中首次读取数据时会记录下这个版本号,当再次读取时通过版本号来取得之前的数据版本。优点是提高了并发度,缺点是在查询时需要进行版本的筛选使用查询性能下降。

4. Serializable:解决了脏读,不可重复读和幻读问题,但效率太低一般不使用。

  当前事务读取到的数据都在表级加锁,这样防止并发事务对此表的任务增加和修改操作,从而实现串行的数据表操作,可以防止幻读。但是效率太低一般不使用。

JDBC的设计


Java标准为连接数据库设计了一整套的接口,如Driver(数据驱动用于获取连接),Connection (数据库连接), DataSource (数据库连接池), ResultSet(结果集)等。众多的数据库厂商只要实现这套接口就可以实现使用Java连接使用数据库的功能。如MySql提交了MySql Connector for Java,提供了MySqlConnection,ResultSetImpl等。c3p0或druid连接池则需要实现DataSource接口。

当开发程序时我们可以只关于接口而不去关注具体的实现类,通过Class.forName("驱动的名称"),通过反射加载驱动类的同时,驱动类的静态代码块儿就会向DriverManager注册自己,之后我们就可以通过DriverManager来拿到MySql的Driver对象了,实现最大限度的松耦合,在我们的代码中不直接引用MySql的类。

在Connection上通过setAutoCommit方法来关闭自动提交即可打开事务。通过setTransactionIsolation方法来设置事务隔离级别。