Java软件开发基础知识梳理之(6)------事务相关知识点

来源:互联网 发布:linux下安装lnmp 编辑:程序博客网 时间:2024/05/21 11:24

一、数据库事务:事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位,用于在多用户并发操作的环境下保证数据的完整性,防止脏数据的产生
二、Java事务类型
1 JDBC事务:使用 JDBC 事务界定时,您可以将多个 SQL 语句结合到一个事务中;JDBC 事务的一个缺点是事务的范围局限于一个数据库连接,一个 JDBC 事务不能跨越多个数据库
2 JTA事务:JTA是一种高层的,与实现无关的,与协议无关的API,应用程序和应用服务器可以使用JTA来访问事务
注:JTA事务需要实现 javax.sql.XADataSource 、javax.sql.XAConnection和javax.sql.XAResource 接口的JDBC驱动程序的支持;一个实现了这些接口的驱动程序将可以参与 JTA 事务,一个 XADataSource 对象就是一个 XAConnection 对象的工厂
3 容器事务: 容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现
事务比较:(1)JDBC事务控制的局限性在一个数据库连接内,但是其使用简单;(2)JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂;(3)容器事务,需要J2EE应用服务器的支持,局限于EJB应用使用
三、数据库的并发操作与事务隔离级别
1 数据库并发操作可能引起的问题
(1) 脏读(事务未提交,修改的数据提前被读取): 脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据
(2) 不可重复读(使用同一个SQL语句,两次读到的结果不一致): 不可重复读是指在同一个事务内,两个相同的查询返回了不同的结果
(3) 幻读(Phantom Reads): 事务在操作过程中进行两次查询,第二次查询结果包含了第一次查询中未出现的数据
(4) 第一类更新丢失(回滚丢失): 当2个事务更新相同的数据源,如果第一个事务被提交,而另外一个事务却被撤销,那么会连同第一个事务所做的跟新也被撤销,也就是说第一个事务做的跟新丢失了
(5) 第二类更新丢失(覆盖丢失): 当2个或这个多个事务查询同样的记录然后各自基于最初的查询结果更新该行时,会造成第二类丢失更新,因为每个事务都不知道不知道其他事务的存在,最后一个事务对记录做的修改将覆盖其他事务对该记录做的已提交的更新
2 事务的隔离级别
(1) 未提交读(READ_UNCOMMITTED)------隔离级别最低,并发性能高
(2) 提交读(READ_COMMITTED)------锁定正在读取的行
(3) 可重复读(Repeatable Read)------锁定所读取的所有行
(4) 串行/序列化(Serializable)------锁表
3 不同隔离级别可能引起的并发问题
隔离级别          脏读   不可重复读   幻读   第一类更新丢失   第二类更新丢失
未提交读           Y            Y               Y             N                          Y
提交读               N           Y                Y             N                          Y
可重复读           N           N                Y             N                          N
串行/序列化      N            N                N            N                           N

四、Spring支持的事务传播特性包括以下7种
1 Propagation.REQUIRED:如果已经处于事务环境中则使用已有的事务,否则开启一个新的事务
2 Propagation.REQUIRES_NEW: 无论是否处于事务环境中,总是开启新的事务
3 Propagation.SUPPORTS: 自身不会开启事务,在事务范围内则使用相同事务,否则不使用事务
4 Propagation.NOT_SUPPORTED: 自身不会开启事务,在事务范围内使用则先挂起事务,等运行完毕后恢复事务
5 Propagation.MANDATORY: 自身不开启事务,必须在事务环境使用否则报错
6 Propagation.NEVER: 不支持事务,如果在事务范围内使用则抛出异常
7 Propagation.NESTED: 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
注:Propagation.NESTED需要JDBC3.0才支持

五、Propagation.REQUIRES_NEW和Propagation.NESTED的区别
1 RequiresNew每次都创建新的独立的物理事务,而Nested只有一个物理事务
2 Nested嵌套事务回滚不一定会导致外部事务回滚(如果外部事务所在的业务方法捕获了内部事务抛出的异常就不会导致外部事务回滚),但外部事务回滚将导致嵌套事务回滚;而 RequiresNew由于都是全新的事务,所以之间是无关联的
3 Nested需要JDBC3.0的支持才能实现(因为需要用到JDBC的SavePoint技术)
4 例子如下
假如有两个业务接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一个方法实现如下
/**
*事务属性:PROPAGATION_REQUIRED
* Desc: 内部事务回滚会导致外部事务回滚
*/
void methodA() {
  ServiceB.methodB();
}

/**
*事务属性:PROPAGATION_REQUIRED
* Desc: 内部事务回滚不会导致外部事务回滚
*/
void methodA() {
   try{
     ServiceB.methodB();
   }catch(Exception e){
     //
   }
}

其中ServiceB的 methodB配置了PROPAGATION_NESTED
5 嵌套事务用于执行分支选择的例子如下
ServiceA {   
    /**
     * 事务属性配置为 PROPAGATION_REQUIRED
     */ 
    void methodA() { 
        try { 
            ServiceB.methodB(); 
        } catch (SomeException) { 
            // 执行其他业务, 如 ServiceC.methodC(); 
        } 
    } 

六、隔离级别与锁机制
1 未提交读(READ_UNCOMMITTED):这种隔离级别读取和更新数据的时候依然会将相应的记录加锁(共享锁/排它锁),执行完查询或更新操作后锁立刻释放,而不是等到事务结束
2 提交读(READ_COMMITTED):共享锁在读取数据之后马上释放,而排他锁则是在事务结束的时候再释放
3 可重复读(Repeatable Read):共享锁和排他锁都是在事务结束的时候再释放的,即一个事务所读取的数据是不会被别的事务更新的,在事务执行过程中任何时候都可以读取刚才读过的数据,直到事务结束
4 串行/序列化(Serializable):共享锁和排他锁都是在事务结束的时候被释放
  注:和Repeatable Read不同的是,Repeatable Read在读取数据的时候只是对所读取的记录加共享锁,而Serializable则是对整个表加共享锁,这样不但读取的数据不会被别的事务修改,在同一个表中的其他未被读取的数据也不会被修改,甚至不用担心读到新加入这个表中的数据(根本无法往表中加数据),所以这种隔离级别是不会出现虚读的

七、悲观锁与乐观锁
1 悲观锁: 利用数据库本身的锁机制实现
  select * from table_name where ...  for update
2 乐观锁:  利用程序处理并发,方式大概有
(1)版本号
(2)时间戳摧
(3)对将要更新的数据进行提前读,并事后对比

0 0
原创粉丝点击