`
lianxiangbus
  • 浏览: 528303 次
文章分类
社区版块
存档分类
最新评论

危险的Hibernate映射共用class及其解决

 
阅读更多

使用hibernate对于有交集字段结构或结构一样的表,有时候会很“正常”的想到一种“偷懒”的办法,就是让一个hbm映射定义映射到多个TABLE。例如表维表(或代码表)有结构:

<class name="LabelValueCodeBase" abstract="true">
<meta attribute="sync-DAO">false</meta>
<composite-id name="id" class="LabelValueCodePK">
<key-property name="value" column="VALUE" type="string" />
<key-property name="langFlag" column="LANG_FLAG" type="string" />
</composite-id>

<property name="label" column="LABEL" type="string" not-null="true" length="50" />
</class>

那么可能对该结构进行扩充出几个同样的hbm定义:
<union-subclass
name="my.model.LabelValueCode"
extends="LabelValueCodeBase"
entity-name="yesNoCode"
table="YES_NO_CODE"
/>

<union-subclass
name="my.model.LabelValueCode"
extends="LabelValueCodeBase"
entity-name="sexCode"
table="SEX_CODE"
/>

然后在一个事务里查询此两表,例如:
String hintStr = "您" + LabelValueCodeDAO.getYesNodeByValue("0").getLabel() + "是管理员用户,性别:" + LabelValueCodeDAO.getYesNodeByValue("0").getLabel();

如果 YES_NO_CODE 表里,字符串"0"对应的编码是“不是”,而SEX_CODE表里字符串"0"对应的的编码是“男”。那么我期望的结果是显示:您不是管理员用户,性别:男

但是实际运行的结果变成了:您不是管理员用户,性别:不是

这样就让人纳闷了,难道查询出错了?打开hibernate输出各SQL功能在控制台看到的SQL在查询器里执行,也是获得正确的结果。最后仔细一行行的跟踪,突然发现忽略了一个重大问题。就是hibernate一级缓存机制。

hiberante一级缓存是根据class的OID进行命中检测的,而两个表使用的是同一个class,因此,查询getYesNodeByValue("0")时,查询了VALUE为0,LANG_FLAG为zh_CN的对象并加入缓存。而随后的getYesNodeByValue("0")查询对象的OID也是VALUE为0,LANG_FLAG为zh_CN,于是把前一次查询的对象在cache命中了!于是便成了实际的错误输出结果。

因此,解决办法是,要么采用不同的class来对不同的表进行管理,要么就是。在查询出来对象后,使用evict强制从hibernate session中解除:

stringBuffer.append("您");
LabelValueCode labelValueCode = LabelValueCodeDAO.getYesNodeByValue("0");
stringBuffer.append(labelValueCode.getLabel());
getSession().evict(labelValueCode);
stringBuffer.append("是管理员用户,性别:");
stringBuffer.append("LabelValueCodeDAO.getYesNodeByValue("0").getLabel();

这样便得到了期望的结果。

此错误“症状”仅在多表映射一个class文件并且ID机制一样,并在一个事务使用不同表时才发生,常见于查询多表长事务或open session in view方式。如果多次查询不在一个事务中,则不会存在这个问题。因此多表映射共用class必须小心使用。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics