危险的Hibernate映射共用class及其解决
2008-08-05 10:07
183 查看
使用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必须小心使用。
<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必须小心使用。
相关文章推荐
- 关于ClassNotFoundException: org.hibernate.hql.ast.HqlToken 问题的解决
- Hibernate java.lang.ClassCastException ERROR解决方法
- Hibernate解决原生SQL映射为对象的问题探究
- java.lang.ClassNotFoundException: org.hibernate.Session问题解决
- 举例说明Android开发中遇到的 java.lang.ClassCastException: java.lang.String这个问题的原因及其解决办法
- 解决Weblogic与Hibernate的antlr-x.x.jar冲突引起的ClassNotFoundException: org.hibernate.hql.ast.HqlToken问题
- hibernate中继承映射的三种方式(SINGLE_TABLE,TABLE_PER_CLASS,JOINED)
- Hibernate一对多单向关联和双向关联映射方法及其优缺点
- No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer解决方法
- hibernate.cxf.xml 配置文件 解决 内容必须匹配 "(property*,mapping*,(classcachecollectioncache)*,event*,listener*)
- org.hibernate.ObjectNotFoundException异常的分析及其解决办法
- 解决Hibernate原生SQL映射问题
- 解决Mybatis配置ORM映射使用javaType=Date.class时候时分秒都为0
- 解决:Weblogic容器、SSH框架下Hibernate映射Oracle XMLType方法与Jar包冲突
- Hibernate 实体类 映射文件使用注解方式 经典报错以及解决方法
- 解决weblogic抛出的ClassNotFoundException: org.hibernate.hql.ast.HqlToken异常
- 解决Hibernate原生SQL映射问题 - SQL查询出来的结果映射为值对象
- Hibernate:组合模式解决树的映射
- ClassNotFoundException: org.hibernate.hql.ast.HqlToken解决之道
- myeclispe 6.5生成hibernate映射时产生“generating artifacts”错误的解决!