我遇到了Hibernate的奇怪行为。我已经在墙上撞了一段时间了,并且会给出任何答案,从而获得+100赏金的解决方案。
我有一个JAX-RS(Jersey)REST服务器,带有一个过滤器,可以根据请求关联一个Hibernate会话。
在一个请求中,客户端使用一个会话(和一个事务)POST一些存储在数据库中的数据。在后续调用中,客户端尝试获取此实体,但Hibernate无法找到它。
一些观察结果:
如果我同时运行多个客户端,我只能重现这个。我从来没有设法通过一次运行一个客户端来重现它。)
我可以查看数据库中的实体ID,如果我重新启动服务器,Hibernate就会找到该实体。
如果我使用大小为1的线程池(无论我同时运行多少个客户端),都不会发生错误。
这是代码,有一些日志记录:
chargeables.setId(new SecureRandom().nextLong());
System.out.printf("%s, session: %s [%s]%n",
Thread.currentThread(),
System.identityHashCode(session),
"session.beginTransaction()");
session.beginTransaction();
System.out.printf("%s, session: %s [%s]%n",
Thread.currentThread(),
System.identityHashCode(session),
"session.save(id = "+chargeables.getId()+")");
session.save(chargeables);
System.out.printf("%s, session: %s [%s]%n",
Thread.currentThread(),
System.identityHashCode(session),
"session.getTransaction().commit()");
session.getTransaction().commit();
获取实体的代码:
System.out.printf("%s, session: %s [%s]%n",
Thread.currentThread(),
System.identityHashCode(session),
"session.get("+id+")");
Chargeables entity = (Chargeables) session.get(Chargeables.class, id);
if (entity == null)
System.out.printf("%s, session: %s [%s]%n",
Thread.currentThread(),
System.identityHashCode(session),
"ENTITY NOT FOUND!");
现在这里是结果日志的摘录(带有一些额外的打开/关闭会话输出):
Thread[Grizzly(5),5,main], session: 2041842357 [factory.openSession()]
Thread[Grizzly(5),5,main], session: 2041842357 [session.beginTransaction()]
Thread[Grizzly(5),5,main], session: 2041842357 [session.save(id = 7939229356942262438)]
Thread[Grizzly(5),5,main], session: 2041842357 [session.getTransaction().commit()]
Thread[Grizzly(5),5,main], session: 2041842357 [session.close()]
[...]
Thread[Grizzly(7),5,main], session: 1717445911 [factory.openSession()]
Thread[Grizzly(7),5,main], session: 1717445911 [session.get(7939229356942262438)]
Thread[Grizzly(7),5,main], session: 1717445911 [ENTITY NOT FOUND!]
Thread[Grizzly(7),5,main], session: 1717445911 [session.close()]
为什么我到达ENTITY NOT FOUND!
?
Hibernate版本:4.1.9.Final
MySQL verison:14.14 Distrib 5.5.29
Chargeables
的映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping
default-cascade="all"
package="se.package.common.chargeables"
default-lazy="false">
<class name="Chargeables">
<id name="id" type="long">
<generator class="assigned"/>
</id>
<property name="startTimeStamp" />
<property name="endTimeStamp" />
<list name="chargeables">
<key column="chargeableId" />
<list-index column="pos" />
<many-to-many class="Chargeable"/>
</list>
</class>
<class name="Chargeable">
<id column="id" type="long">
<generator class="native"/>
</id>
<discriminator />
<property name="timestamp" />
</class>
<subclass name="DataTransfer" extends="Chargeable">
<property name="bytesSent" />
<property name="bytesReceived" />
</subclass>
<subclass name="TelephonyChargeable" extends="Chargeable">
<many-to-one name="num" />
</subclass>
<subclass name="Call" extends="TelephonyChargeable">
<property name="duration" />
</subclass>
<subclass name="OutgoingCall" extends="Call" />
<subclass name="IncomingCall" extends="Call" />
<subclass name="Message" extends="TelephonyChargeable" />
<subclass name="Sms" extends="Message" />
<subclass name="IncomingSms" extends="Sms" />
<subclass name="OutgoingSms" extends="Sms" />
<subclass name="Mms" extends="Message" />
<subclass name="IncomingMms" extends="Mms" />
<subclass name="OutgoingMms" extends="Mms" />
</hibernate-mapping>
答案 0 :(得分:0)
恕我直言,这是一个孤立的问题。您的第二个会话在第一个会话的事务提交之前开始。由于默认的hibernate隔离级别是read_commited,第二个会话无法检索实体。
如何在两个线程之间传递id?
答案 1 :(得分:0)
如果您可以在数据库中看到提交,那么它在MySQL级别就不是一个隔离问题:控制台操作也是客户端操作,运行它们时没有特殊的权限,因此它们符合您的隔离策略选择。
寻找解决方案,我发现Hibernate provide some facilities to cache DB results:实际上实现了2级缓存。也许您的hibernate安装捆绑了缓存机制?
第一级基于每个会话工作,这与您在一个线程上运行代码时观察到的行为一致,并且看起来恰好在默认情况下被激活。我不是熟练的Hibernate,我不能肯定地说,但我的意见是你的设置将第一级缓存设置为只读模式,而你希望它处于读写模式。