0 事务的概念:
事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。
例如:A——B转帐,对应于如下两条sql语句
update from account set money=money+100 where name=‘b’;
update from account set money=money-100 where name=‘a’;
数据库开启事务命令
start transaction 开启事务
Rollback 回滚事务
Commit 提交事务
1 事务特性:
a) 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
b) 一致性(Consistency)事务必须使数据库从一个一致性状态变换到另外一个一致性状态。eg: 用户a,b各有1000元,a给b转账500结束后,a和b的账户总额还是2000没有变更,这就是操作前和操作后的状态一致
c) 隔离性(Isolation)事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
d) 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
2 如果不考虑事务隔离性,会导致的问题:
a) 脏读,(一个事务读取了另外一个事务未提交的数据) eg: 甲开启事务,给乙打钱,后给乙打电话要求发货,乙查询有钱后发货给甲,然后甲将事务回滚
b) 不可重复读(两次读取同一条记录,读到的结果不一致) eg: 甲余额200元,银行查询甲余额得到200,此时甲存100,银行再次查询得到结果为300,银行两次查询结果不一致,这种结果在一些特定场景出现是有问题,比如做报表前后两次得到的结果不一样
c) 虚读/幻读(在同一个事务内,第一次读到5条记录,第二次读到6条记录,多读出来的那条记录叫幻影数据,)
不可重复读和虚读/幻读的最大区别是: 前者是针对同一条记录而言,后者是针对记录个数而言(个人理解)
3 数据库定义的4中事务隔离级别(不同级别来防止不同问题)
隔离级别 | 描述 | 问题 |
read uncommitted读未提交数据 | 允许事务读取未被其他事务提交的变更
| 脏读/不可重复读/幻读的问题都会出现 |
read committed读已提交数据 | 事务只能读取被其他事务已提交的变更记录 | 避免了脏读; 不可重复读/幻读会出现 |
repeatable read可重复读 | 确保事务可以多次从一个字段中读取相同数值,在这个事务持续期间,禁止其他事务对此字段进行更新 | 避免了脏读/不可重复读; 幻读会出现 |
serializable 串行化 | 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对此字段进行插入,更新,删除 | 性能低下 规避了所有问题 每一个事务操作都会锁表,单线程操作,事务提交后才能让下一个事务/线程进来操作这个表 |
mysql支持以上所有隔离级别,默认的隔离级别为: repeatable read可重复读
oracle 支持2种离级别,read committed 和 serializable, 默认隔离级别为: read committed
oracle的默认事务隔离级别设置的要比mysql低。
mysql隔离级别查询语句:
设置mysql事务隔离级别语句:
4 JDBC使用事务:
当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句
若想关闭这种默认提交方式,让多条SQL在一个事务中执行,使用如下写法:
Connection.setAutoCommit(false);
JDBC控制事务语句
Connection.setAutoCommit(false);
Connection.rollback();
Connection.commit();
5 JDBC设置事务回滚点:
Savepoint sp = conn.setSavepoint();
Conn.rollback(sp);
Conn.commit(); //回滚后必须要提交
6 JDBC设置事务级别:
是使用Connction的setTransactionIsolation(级别)方法,具体可参看 javaseapi的Connection介绍
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
Savepoint sp = null;
try {
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);
conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
String sql = "select money from account where name='aaa'";
st = conn.prepareStatement(sql);
rs = st.executeQuery();
if(rs.next()){
System.out.println("aaa有" + rs.getDouble("money") + "钱!!!");
}
Thread.sleep(1000*20); // 在线程等待这20S内,去数据库中修改操作语句
rs = st.executeQuery();
if(rs.next()){ // 查询金额是否变更
System.out.println("aaa有" + rs.getDouble("money") + "钱!!!");
}
conn.commit();
}catch(Exception e){
e.printStackTrace();
}
finally {
bj.JdbcUtils.release(rs, st, conn); // 自定义工具类
}
}