hibernate 控制并发访问

本文详细介绍了在 Hibernate 中如何处理并发访问的问题,通过示例展示了乐观锁的使用,包括版本控制和时间戳策略。在并发更新同一记录时,可能出现 StaleObjectStateException,文中给出了正确处理并发的示例代码,帮助理解 Hibernate 的并发控制机制。
摘要由CSDN通过智能技术生成
在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同: 
◆未授权读取(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。 

◆授权读取(Read Committed):允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。 

◆可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。 

◆序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。 


如果一个事务读取由另一个还没有被提交的事务进行的改变,就发生脏读取(dirty read)。这很危险,因为由其他事务进行的改变随后 
可能回滚,并且低一个事务可能编写无效的数据。 

如果一个事物读取一个行两次,并且每次读取不同的状态,就会发生不可重复读取(unrepeatable read)。例如,另一个事务可能已经 
写到这个行,并已经在两次读取之间提交。 

不可重复读取的一个特殊案例是二次丢失更新问题(second lost updates problem)。想象两个并发事务都读取一个行:一个写到行并 
提交,然后第二个也写到行并提交。有第一个事务所做的改变丢失了。 

幻读(phantom read)发生在一个事务执行一个查询两次,并且第二个结果集包括第一个结果集中不可见的行,或者包括已经删除的行时。 
这种情况是由另一个事务在两次查询执行之间插入或者删除行照成的。 


ANSI事务隔离性级别: 
允许脏读取但不允许丢失更新的系统,要在读取未提交(read uncommitted)的隔离性中操作。如果一个为提交事务已经写到一个行, 
另一个事务就不可能再写到这个行。但任何事务都可以读取任何行。这个隔离性级别可以在数据库管理系统中通过专门的写锁来实现。 

允许不可重复读取但不允许脏读取系统,要实现读取提交(read committed)的事务隔离性。这可以用共享的读锁和专门的写锁来实现。 
读取事务不会阻塞其他事务访问行。但是为提交的鞋事务隔赛所有其他的事务访问改行。 

在可重复读(repeatable read)隔离性模式中操作的系统既不允许不可重复读取,也不允许脏读取。幻读可能发生。 

可序列化 (serializable)提供最严格的事务隔离性。这个隔离性级别模拟连续的事务执行,好像事务是连续的一个接一个的执行,而不是 
并发的执行。 




选择隔离性级别: 
首先,消除读取为提交隔离性级别。在不同的事务中使用一个为提交的事务变化是很危险的。一个事务的回滚或者失败将影响其他的并发事务。 
甚至由一个终止回滚的事务所做的改变也可能在任何地方被提交,因为他们可能读取,然后由另一个成功的事务传播! 
其次,大多数应用程序不需要可序列化的隔离级别(幻读通常不成问题),并且这个隔离性往往难以伸缩。 
这样就把选择读取提交还是可重复读取交给你了。 






Hibernate配置选项: 
hibernate.connection.isolation = 4 
1-读取为提交隔离性 
2-读取提交隔离性 
3-可重复读取隔离性 
4-可序列化隔离性 







乐观并发控制: 
乐观的方法始终假设一切都会很好,并且很少有冲突的数据修改。在编写数据时,乐观并发控制值在工作单元结束时才出现错误。多用户 
的应用程序通常默认为使用读取提交隔离性级别的乐观并发控制和数据库连接。只有适当的时候才获得额外的隔离性保证;这种方法保证 
了最佳的性能和可伸缩性。 


对于如何处理对话中这些第二个事务中的丢失更新,你有3种选择: 
最晚提交生效(last commit wins)--两个事物提交都成功,第二个提交覆盖第一个变化。没有显示错误消息。 
最早提交生效(fist commit wins)--对话A的事务被提交,并且在对话B中提交事务的用户得到一条错误信息。用户必须获取新数据来 
重启会话,并在此利用没有失效的数据完成对话的所有步骤。 
合并冲突更新(merge conflicting updates)--第一个修改被提交,并且对话B中的事务在提交时终止,带有一条错误消息。但是失败 
的对话B用户可以选择性的应用变化,而不是再次在对话中完成所有工作。 





在hibernate中启用版本控制: 
hibernate提供自动的版本控制。每个实体实例都有一个版本,它可以是一个数字或者是一个时间戳。当对象修改时,hibernate就增加 
他的版本号,自动比较版本,如果侦测到冲突就抛出异常。因此,你给所有持久化的实体类都添加这个版本属性,来启动乐观锁: 
version:
  1. public class Item implements Serializable {  
  2.     private Integer itemId;  
  3.     private String itemName;  
  4.       
  5.     /** 版本控制 */  
  6.     private int version;  
  7.       

配置:
  1. <class name="Item" table="ITEM">  
  2.     <id name="itemId" column="ITEM_ID" type="integer">  
  3.         <generator class="native"/>  
  4.     </id>  
  5.     <!-- XML格式的version属性映射必须立即放在标示符属性映射之后 -->  
  6.     <version name="version" access="field" column="OBJ_VERSION" />  
  7.       
  8.     <property name="itemName" type="string" column="ITEM_NAME"/>  
  9.       
  10. </class>  

代码:
  1.     /** 
  2.      * 报错: 
  3.      * org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) 
  4.      * @param itemName 
  5.      */  
  6.     private static void update1(String itemName) {  
  7.         Configuration configuration = new Configuration().configure();  
  8.         SessionFactory sessionFactory = configuration.buildSessionFactory();  
  9.         Session session = sessionFactory.openSession();  
  10.         Transaction tr = session.beginTransaction();  
  11.           
  12.         Item item = (Item) session.get(Item.class1);  
  13.         item.setItemName(itemName);  
  14.           
  15.           
  16.         Session session1 = sessionFactory.openSession();  
  17.         Transaction tr1 = session1.beginTransaction();  
  18.           
  19.         Item item1 = (Item) session1.get(Item.class1);  
  20. //      一样会报错  
  21. //      item1.setItemName(itemName);  
  22.   
  23.         item1.setItemName(itemName+"1");  
  24.           
  25.           
  26.         tr.commit();  
  27.         session.close();  
  28.           
  29.         //org.hibernate.StaleObjectStateException:  
  30.             //Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)  
  31.         tr1.commit();  
  32.         session1.close();  
  33.           
  34.         sessionFactory.close();  
  35.     }  
  36.       
  37.       
  38.     /** 
  39.      * 提交成功 
  40.      * @param itemName 
  41.      */  
  42.     private static void update2(String itemName) {  
  43.         Configuration configuration = new Configuration().configure();  
  44.         SessionFactory sessionFactory = configuration.buildSessionFactory();  
  45.         Session session = sessionFactory.openSession();  
  46.         Transaction tr = session.beginTransaction();  
  47.           
  48.         Item item = (Item) session.get(Item.class1);  
  49.         item.setItemName(itemName);  
  50.           
  51.         tr.commit();  
  52.         session.close();  
  53.           
  54.         Session session1 = sessionFactory.openSession();  
  55.         Transaction tr1 = session1.beginTransaction();  
  56.           
  57.         Item item1 = (Item) session1.get(Item.class1);  
  58.         item1.setItemName(itemName+"1");  
  59.           
  60.         tr1.commit();  
  61.         session1.close();  
  62.           
  63.         sessionFactory.close();  
  64.     }  
  65.       


timestamp(理论上讲,时间戳更不安全。且在集群环境中更不安全):
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值