一、事务的概念
事务指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败。
事务的特性:
①原子性:是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
②一致性:指事务执行前后数据的完整性必须保持一致
③隔离性:指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离
④持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不应该对其有任何影响。
二、事务的隔离级别
多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。
如果数据库没有事务隔离机制,事务不考虑隔离性,可能会引发如下问题:
①脏读:指的是一个事务读取了另外一个事务未提交的数据
比如A和B进行银行转账,当B刚转了账,还没确认(即还未提交)的时候,这时A查看了账户,发现账户上的金额多了,这就是脏读。
②不可重复读:指的是在一个事务内读取表中的某一行数据,多次读取的结果不同。
比如银行查询A的余额,刚查的时候为200元,这时候,A存入100元,并且提交了,这时银行又查询一次,结果发现A账户为300元,两次查询结果不一样,就会产生疑惑。
不可重复读和脏读的区别是:脏读是读取前一次事务未提交的脏数据,不可重复读是重新读取了前一事务已经提交的数据。
③幻读(虚读):指的是在一个事务内读取到了别的事务插入的数据,导致前后结果不一样。
比如A存入100元还未提交,银行统计总共有多少存款发现是500,这时A提交了,银行又查询一次,发现总共存款变成了600。
三、JDBC中使用事务
①Connection.setAutoCommit(false):开启事务(start transaction),关闭自动提交
②Connection.rollback():回滚事务(rollback),如果出现异常,也可以自动回滚
③Connection.commit():提交事务(commit)
四、代码展示
演示银行转账
update account set money=money-100 where name='A'
update account set money=money+100 where name='B'
先建立一张表account:
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`money` double DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
代码:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 模拟转账成功
* @author littledyf
*
*/
public class TransferSuccess {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///spring_transaction", "root", "1234");
conn.setAutoCommit(false);
//A账户减少100
String sql1 = "update account set money=money-100 where name='A'";
ps = conn.prepareStatement(sql1);
ps.executeUpdate();
//B账户增加100
String sql2 = "update account set money=money+100 where name='B'";
ps = conn.prepareStatement(sql2);
ps.executeUpdate();
conn.commit();
System.out.println("转账成功!");
} catch (Exception e) {
try {
conn.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}finally {
try {
if(ps != null){
ps.close();
}
if(conn != null){
conn.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
在模拟转账的代码中,如果程序出现异常,会发生回滚,也可以设置事务的回滚点,使用以下方式:
Savepoint sp = conn.setSavepoint();
Conn.rollback(sp);
Conn.commit();//回滚后必须通知数据库提交事务