Hibernate事务异常,查询和更新并存卡死表

来源:互联网 发布:windows api 创建窗体 编辑:程序博客网 时间:2024/06/06 05:55

        最近项目中遇到一个问题:查询和更新事务异步运行,当并发出现在某个表上时,SQLServer数据库会将该表锁死,导致程序无法执行。但是Oracle和MySql不会。项目采用Hibernate作为ORM框架,问题集中在多对多映射上。

 

      项目问题暴露和解决顺序如下:

系统中有两个实体类,A类和B类,它们之间为多对多关联。项目目标是根据A的名称,来找到B,然后将B删除。如果在A下面当前只有一条B记录那么不会出现问题,但是如果有多条B记录那么将会产生如下异常:a different object with the same identifier value was already associated with the session。错误原因:在hibernate中同一个session里面有了两个相同标识但是是不同实体。
解决方法:session.clear();

 

      之所以能够在同一个session中出现不同的对象,因为我们在根据A删除B时并没有提交事务,第一次我们查询出来B并且级联出来A将其放到session中,此时将A和B的关系断开,但是并没有提交事务,当再次断开A和另外一个B对象时,犹豫两个B对象都将A放到了session中导致出现了上述异常。通过每次断开关系后执行clear能够将session中A对象清空。但是可能会产生如下异常:"Found two representations of same collection"异常。该异常原因同上。

解决方法:采用本地sql的方式。

 

但是很不幸,原先的单元测试都是在Oracle上,以为都解决了问题,但是当换到SQLServer上之后,出现了将表卡死的现象。问题原因如第一段描述。该问题通过理解数据库的事务隔离级别来定位,既然是不同的事务并发导致的死锁,那么我们只要将所有的操作放到同一个事务中就没有问题了,并且不会出现上述描述的异常,原因猜测是由于是同一个session那么hibernate会将两个不同对象(不同地址)进行merge为同一个对象。这样所有的问题迎刃而解。

-------------------------------------------------------------------------------------------------------------------------

后记:Hibernate可以自定义事务隔离级别,在hibernate.cfg.xml中可以配置

1:读操作未提交(Read Uncommitted)
2:读操作已提交(Read Committed)
4:可重读(Repeatable Read)
8:可串行化(Serializable)

因此,数字4表示“可重读”隔离级别。如果要使以上语句有效,应把此语句行前的注释符“#”去掉:

#hibernate.connection.isolation 4      

也可以在配置文件hibernate.cfg.xml中加入以下代码:

把隔离级别设置为4

          <session-factory>
          <property name=” hibernate.connection.isolation”>4</property>
             ……
          </session-factory>

 

当创建Connection对象时,其事务隔离级别取决于驱动程序,但通常是所涉及的数据库的缺省值。用户可通过调用setIsolationLevel方法来更改事务隔离级别。新的级别将在该连接过程的剩余时间内生效。要想只改变一个事务的事务隔离级别,必须在该事务开始前进行设置,并在该事务结束后进行复位。我们不提倡在事务的中途对事务隔离级别进行更改,因为这将立即触发commit方法的调用,使在此之前所作的任何更改变成永久性的。