分类目录归档:Hibernate

解决Hibernate One-to-one 关系 “No row with the given identifier exists:” 错误


在项目中使用Hibernate One-to-one配置了一些表关系。主表为A表,附带了两个One-to-one关系的字表:B表、C表。

在业务上,A表插入和更新的时候会同时更新B表,但是C表使用另外的业务维护。最近在进行级联删除的时候,提示:

No row with the given identifier exists:

出现这个错误的原因是,删除的时候查找C表数据时,没有找到数据。要解决这个问题的关键点是,在级联删除时,如果one-to-one子表不存在数据,则不进行删除。关于这块的信息在Hibernate手册中并未提及,只是有一个constrained字段设置为true|flase的时候会影响删除的顺序。在A表中尝试把C表的one-to-one关系constrained字段设置为false,再次对A表进行级联删除时,C表有对应的数据才会执行删除操作,无数据时不会执行删除操作,问题解决。

有一点疑问是,在Hibernate中many-to-one这样的关系中可以设置not-null属性,为什么one-to-one属性不能也使用这样的属性呢?使用一个constrained这样的字段,怪别扭的。

——看来只能通过阅读源代码才能知道one-to-one的constrained属性究竟做了什么了。

如何正确释放Hibernate Session使用的资源


最近一个项目使用的SpringMVC+SpringSecurity+Hibernate的开发框架,由于需要使用到多个视图表现方式,所以在Web.xml中针对不同的视图表现方式配置了不同的初始化入口,这直接导致一个项目中存在多个连接缓冲池,其中SpringSecurity单独使用一个连接缓冲池,业务代码单独使用一个,这就引出来下面的问题。

在业务代码中,由于在Service层进行了事务控制,所以业务代码的DAO继承HibernateDaoSupport之后,直接使用getSession().createSQLQuery的方式操作数据库,不用自己管理session。

而在SpirngSecurity中,由于自己实现了UserDetailsService接口,在自定义的UserDetailsService中查询了数据库,获得用户信息。在做压力测试的时候发现很容易就出现can’t open connection的问题,并且在停止压力测试之后,已经打开的链接并不不会被关闭。通过检查代码发现,在UserDetailsService中并没有进行事务控制,也没有对Session进行释放资源的操作,问题就出在这里了。

由于业务代码使用了事务控制,在事务结束的时候框架会释放Session关联的资源,而UserDetailsService中没有事务控制,所以我们需要手动进行Session相关资源的释放。将getSession().createSQLQuery修改为Session sess = getSession(); sess.createSQLQuery,查询完数据之后执行super.releaseSession(sess);释放Session资源。这样子操作之后,没有再出现无法打开连接的问题,并且在Connection空闲指定时间之后,自动关闭了。

进一步阅读super.releaseSession()方法的代码发现,在HibernateDaoSupport中是通过SeesionFacotryUtils.releaseSession(Session, SessionFactory)进行Session资源释放的,在这个方法中,对Session进行了是否存在事务的判断,如果Session本身仍然在事务内,则不关闭。想必,如果业务层存在事务控制,同时在DAO进行releaseSession的操作时,Session并不会真正的被关闭,而要等到事务结束的时候由事务进行关闭操作了。

Hibernate + c3p0 出现APPARENT DEADLOCK!!!错误


最近一个项目用到了SpringMVC+Hibernate,Hibernate的连接缓冲池用的是c3p0。项目快结束了,做了一个压力测试,发现c3p0报APPARENT DEADLOCK!!!错误。网上查了查,在hibernate官方网站发现如下内容:

Mark,

May the StatementCache be with you.

Regarding your deadlocks, they are interesting. The tasks that are failing to terminate are not the Statement cache tasks, but Connection close() tasks. Nevertheless, past deadlocks have occurred because of Statement close was happening simultaneous to Connection close(), and I’m suspicious that we are seeing more of the same here, since you are clearly churning though a lot of PreparedStatements. (You have a zillion pending statement close tasks, and three Connection close tasks that are failing.)

0.9.0 cleared up the most obvious bad interaction between Statement and Connection close(), but it occurs to me that the strategy does not yet guarantee that a PreparedStatement and Connection close cannot occur asynchronously and near simultaneously by happenstance, especially when Statements are being close()ed quite frequently. So, my job is to tighten that up. [Even though in theory it should be foreseen and handled cleanly, many drivers don’t like Statements to be used simultaneous to Connection ops or after Connection close().]

Your job is this: 1) take moonlight’s suggestion for just long enough to persuade yourself that the statement cache is implicated in the apparent deadlock, and write back to let me know the problem goes away when statement caching is turned off; 2) try making max_statements much, much larger, or set it to 0 and set c3p0.maxStatementsPerConnection in c3p0.properties to as many distinct PreparedStatements as your application ever uses. This will both improve the performance of your application (statement caching doesn’t much help if you are always churning through the statements), and make the deadlock you are seeing, presuming it is an artifact of statement close simultaneous with connection close, much less likely. The only likely downside of a large max_statements is the potential memory footprint (see how it works for you), but there may be many upsides. Do give it a shot.

Good luck!
Steve (c3p0 guy)

最后通过在c3p0的配置文件中指明maxStatements = 0以及maxStatementsPerConnection = 0解决了APPARENT DEADLOCK!!!的错误