事务
一】概念:事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。
(每种数据库都有事务的支持,但支持的强度不同)
二】SQL语句操作事务命令
1)开启事务
START TRANSACTION
2)回滚事务
ROLLBACK
3)提交事务
COMMIT
code:
START TRANSCATION; DELETE FROM user where id>50; ROLLBACK; COMMIT;
注意:
1> 在事务范围内回滚是允许的,但如果COMMIT后再回滚无效。
2> 其实每条SQL都有事务存在,只是数据库默认将此事务隐藏起来了
三】JDBC控制事务语句
步骤:
1)设置事务为非自动提交
Connection.setAutoCommit(false);
2)进行回滚操作,一般放在catch语句中
Connection.rollback();
3)进行事务的提交
Connection.commit();
code:
转账案例:jack-->merry conn = JDBCUtils.getConnection(); try { //设置事务为非自动提交 conn.setAutoCommit(false); pstmt = conn.prepareStatement(TranSqlMapping.JACK_OUT_SQL);//UPDATE test_transcation SET salary=salary-1000 WHERE name='jack' pstmt.executeUpdate(); int i = 10 / 0; pstmt = conn.prepareStatement(TranSqlMapping.MERRY_INT_SQL);//UPDATE test_transcation SET salary=salary+1000 WHERE name='merry' pstmt.executeUpdate(); //提交事务 conn.commit(); } catch (SQLException e) { try { // 当出错时进行“回滚事务”操作 conn.rollback(); conn.commit(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); } finally { JDBCUtils.closeAll(rs, pstmt, conn); }
四】设置事务的回滚点
1)设置一个回滚点
Savepoint Connection.setSavepoint();
2)回滚到指定的回滚点
void Connection.rollback(Savepoint sp);
总结:
1)事务的操作必须针对同一个Connection
2)事务的操作中可以设置一个回滚点,可以回滚到特定的回滚点处
五】事务的特性(ACID)
1>原子性(A):事务中的各个操作是一个不可分割的子操作,必须将其看成一个整体。
2>一致性(C):事务前后,由一个一致状态转移到另一个一致状态
*3>隔离性(I):事务中,不同线程操作同张表同条记录时,互相隔离。
4>持久性(D):事务一旦生效,在没有操作该记录时的情况下,永远保持不变
六】三个缺点(未被隔离性)
1>脏读:一个线程看到了另一个线程未提交的数据
2>不可重复读:在一个事务内读取表中的某一个记录,多次读取的结果不同。
/*上述两项:强调的是查询,内容变,但数量不变*/
3>虚读/幻读:在一个事务内读取到了别的事务插入的数据,导致前后结果不一致
/*上述一项:强调的是插入,数量变*/
七】事务的隔离级别——解决三个缺点的办法
1)查看当前事务的隔离级别
SELECT @@TX_ISOLATION;
2)SQL修改当前事务的隔离级别
SET TRANSACTION ISOLATION LEVEL 【事务级别】
eg:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET TRANSACTION ISOLATION LEVEL READ COMMINTTED;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;(默认)
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
3)通过JDBC来设置当前事务的隔离级别
1> void setTransactionlsolation(int level)
参数:level为Connection中的静态属性
static int TRANSACTION_NONE
指示事务不受支持的常量。
*static int TRANSACTION_READ_COMMITTED
指示不可以发生脏读的常量;不可重复读和虚读可以发生。
static int TRANSACTION_READ_UNCOMMITTED
指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。
*static int TRANSACTION_REPEATABLE_READ
指示不可以发生脏读和不可重复读的常量;虚读可以发生。
static int TRANSACTION_SERIALIZABLE
指示不可以发生脏读、不可重复读和虚读的常量。
code:
conn = JDBCUtils.getConnection(); try { // 设置事务的提交方式为非自动提交 conn.setAutoCommit(false); // 设置事务的隔离级别为TRANSACTION_SERIALIZABLE conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); pstmt = conn.prepareStatement(TranSqlMapping.SELECT_ALL); Thread.sleep(10 * 1000); //当另一个线程在进行INSERT等操作的时候此句会阻塞 rs = pstmt.executeQuery(); // 提交事务 conn.commit(); } catch (SQLException e) { try { conn.rollback(); conn.commit(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } finally { JDBCUtils.closeAll(rs, pstmt, conn); }
总结:
1)在项目中,对于SELECT操作不需要事务,对于其它操作(update/delete/insert)操作需要事务。
2)项目中,事务可能在dao层,也可以能在service层无论在哪一层,都必须确保使用的都是同一个Connection
3)为了确保在Service和Dao层中用到的Connection一致,可以使用以下方法:
a)将Service中的Connection传入Dao中
缺点:Service和Dao代码过分耦合
在Service中引入了非业务逻辑操作
b)将JdbcUtils类中的Connection做成单例模式
c)***使用ThreanLocal<T>类将每个线程和自己的Connection绑定在一起,每个线程修改自己的Connection不会影响其它线程的Connection(相当于是副本的模式)
code:
//JDBCUtils
d)在分层结构中,关闭Connection必须要.