3、原生jdbc链接数据库之锁与事务

一。锁的概念
1.作用:是保证数据的一致性,只能一个人修改数据,不能同时多用户修改
2.分类:行级锁和表级锁

     乐观锁和悲观锁

二.事务
1.为了保证数据的一致性和完整性,让数据库的多项操作合并为一个整体,要不全部成功,要不有一个失败全部失败
2.步骤:
  a.将jdbc的自动提交事务改为手动提交
  b.在业务最后提交事务
  c.在异常处理中回滚事务
3.事务的边界:
  事务的开始:jdbc的自动提交事务改为手动提交
  事务的结束:commit和rollback。如果事务没有正常结束,事务中的锁是不会释放的
4.事务隔离级别:
  TRANSACTION_NONE:没有事务
  TRANSACTION_READ_UNCOMMITTED:允许脏读
  TRANSACTION_READ_COMMITTED:避免脏读
  TRANSACTION_REPEATABLE_READ:避免不可重复读
  TRANSACTION_SERIALIZABLE:可重复读
5.事务隔离级别的设定:
  conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);//设置事务级别,默认为该级别

实例:

 1 package com.demo1212;
 2 
 3 import java.sql.Connection;
 4 import java.sql.PreparedStatement;
 5 import java.sql.SQLException;
 6 
 7 public class TanstactionDemo {
 8     //模拟转账流程
 9     public static void main(String[] args) {
10         Connection conn = null;
11         PreparedStatement ps = null;
12         try{
13             conn = JDBCFactory.getConn();
14             //打开事务边界,就是取消自动提交,改为手动
15             conn.setAutoCommit(false);
16             //把一号账户减去500余额
17             String sql1 = "update user_info set balance=balance-500 where user_id=1";
18             ps = conn.prepareStatement(sql1);
19             ps.executeUpdate();
20             
21 //            int num = 1/0;//模拟异常情况
22             
23             //把二号账户加上500余额
24             String sql2 = "update user_info set balance=balance+500 where user_id=2";
25             ps = conn.prepareStatement(sql2);
26             ps.executeUpdate();
27             
28             conn.commit();//提交事务,正常结束
29         }catch (Exception e) {
30             try {
31                 conn.rollback();//有异常发生就回滚事务,是为了保证释放锁
32             } catch (SQLException e1) {
33                 e1.printStackTrace();
34             }
35             e.printStackTrace();
36         }finally {
37             JDBCFactory.closeAll(conn, ps, null);
38         }
39     }
40 }

锁的概念,摘自其他博客...

一、悲观锁和乐观锁

我们经常在开发中遇到数据库并发处理时,处理不一致的问题。需要对程序做并发控制。
典型的并发时出现的冲突有两种:
1、丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。
    例如X=0  A1把X=X+1  A2也设置X=X+1    如果A1和A2同时执行,可能出现最终X=1的情况。而我们需要得到的是X=2  。   这就需要update处理为串行化的。
2、脏读:读到的数据可能是旧的。    初使X=0    A1把X设置为1,A2读取时X是1,但A1后面又修改成了2,或回滚到了0,那么A2读取的数据就是脏数据。
    A2读取的是未提交的数据。一般数据库不会设为读未提交,所以一般不会出现脏读。

那什么是乐观锁,什么是悲观锁。

悲观锁:需要使用数据库的锁机制,如数据库有表级排它锁,有行级排它锁。
假定一切操作都可能发现并发冲突,所以采取悲观态度。通过加锁,屏蔽一切可能违反数据完整性的操作
  比方select * from table for update;  就是表锁,
     select * from table where x = 1 for update; 就是行锁。
当使用for update后,其它会话还是可以执行select操作,但无法执行select xx for update操作,只有当前会话commit后,其它for update操作才会被执行。
典型例子可以参考quartz集群的锁机制:
    http://blog.itpub.net/11627468/viewspace-1764753/ 当然,此时也不可以update,update需要等select xx for update 所在会话commit后才能执行。 注:mysql需要设置autocommit=0 乐观锁:其实不是真实的去锁住记录不让访问,或者不让更新。 假定操作很少发生冲突,一般对于读多写少的情况。只在提交操作时检查是否违反数据完整性。[1] 乐观锁不能解决脏读的问题。 可以通过版本号是否比上个版本号或者时间戳来实现。 对于冲突检测后的处理,需要业务逻辑去处理。 二、排它锁和共享锁 在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。   当数据对象被加上排它锁时,其他的事务不能对它读取和修改。
  加了共享锁的数据对象可以被其他事务读取,但不能修改。   数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。 三、表级锁和行级锁 DML锁的目的在于保证并发情况下的数据完整性,主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。 当Oracle执行DML语句时,系统自动在所要操作的表上申请TM类型的锁。当TM锁获得后,系统再自动申请TX类型的锁,并将实际锁定的数据行的锁标志位进行置位。这样在事务加锁前检查TX锁相容性时就不用再逐行检查锁标志,而只需检查TM锁模式的相容性即可,大大提高了系统的效率。TM锁包括了SS、SX、S、X等多种模式,在数据库中用0-6来表示。不同的SQL操作产生不同类型的TM锁。 值 锁模式 锁描述 SQL 0 NONE 1 NULL 空 SELECT 2 SS(ROW-S) 行级共享锁 其他对象只能查询这些数据行 SELECT FOR UPDATE、LOCK FOR UPDATE、 LOCK ROW SHARE 3 SX(ROW-X) 行级排它锁 在提交前不允许做DML操作 INSERT、UPDATE、DELETE、 LOCK ROW SHARE 4 S(SHARE) 共享锁 CREATE INDEX、LOCK SHARE 5 SSX(S/ROW-X) 共享行级排它锁 LOCK SHARE ROW EXCLUSIVE 6 X(eXclusive) 排它锁 ALTER TABLE、DROP TABLE、DROP INDEX、 TRUNCATE TABLE、LOCK EXCLUSIVE 四、几个问题 1.UPDATE/DELETE操作会将RS锁定,直至操作被COMMIT或者ROLLBACK; 若操作未COMMIT之前其他session对同样的RS做变更操作,则操作会被hold,直至前session的UPDATE/DELETE操作被COMMIT; 2.session内外SELECT的RS范围 前提:INSERT、UPDATE操作未COMMIT之前进行SELECT; 若在同一session内,SELECT出来的RS会包括之前INSERT、UPDATE影响的记录; 若不在同一session内,SELECT出来的RS不会包括未被COMMIT的记录; 3.SELECT.... FOR UPDATE [OF cols] [NOWAIT/WAIT] [SKIP LOCKED] OF cols:只锁定指定字段所在表的RS,而没有指定的表则不会锁定,只会在多表联合查询时出现; NOWAIT:语句不会hold,而是直接返回错误ORA-00054: resource busy and acquire with NOWAIT specified; WAIT N:语句被hold N秒,之后返回错误ORA-30006: resource busy; acquire with WAIT timeout expired; SKIP LOCKED:不提示错误,而是直接返回no rows selected; 以上几个选项可以联合使用的,比较推荐的有: SELECT.... FOR UPDATE NOWAIT:对同一RS执行该SQL时,直接返回错误; SELECT.... FOR UPDATE NOWAIT SKIP LOCKED:对同一RS执行该SQL时,直接返回空行; PS:当RS被LOCK住之后,只对同样请求LOCK的语句有效,对无需LOCK的SELECT语句并没有任何影响; 以上悲观锁和乐内容参考: http://www.cnblogs.com/guyufei/archive/2011/01/10/1931632.html spring锁实现参数: http://blog.itpub.net/12158104/viewspace-374745 关于隔离级别可以参考: http://blog.itpub.net/11627468/viewspace-1793036/ 关于数据库的锁可以参考: http://zhidao.baidu.com/link?url=zRnaslJ8INtEviT--BzrT2bMOqf4LJQzL-NQg2ECu6l-s-xPHi11bBlNjN2_zyNrwd9M0ZnbelQntmfYPB0ifq
原文地址:https://www.cnblogs.com/wlxslsb/p/10722494.html