Dam*_*ash 3 mapping collections hibernate
欢迎,
我对Hibernate映射有一些问题.
数据库结构:
TableA
-ID_A --PK
TableB
-ID_B --PK
-ID_A -- FK -> TableA
TableC
-ID_C -- PK
-ID_A -- FK -> TableA
Run Code Online (Sandbox Code Playgroud)
POJO结构:
class TableA extends Pojo {
/*Some Fields*/
}
class TableB extends Pojo {
TableA tableA;
/*Some properties*/
}
class TableC extends Pojo {
TableA tableA;
Collection<tableB> tableBs;
}
Run Code Online (Sandbox Code Playgroud)
我想要的是TableB Pojo映射中TableB元素的集合,映射键是tableA.
此集合应该是只读的.
映射应该是hbm而不是注释.
我可能已经为每一种可能的方式完成了这个...我得到的结果是当我操作一个TableC对象然后一切都正确但是如果我加载它们的集合然后只有最后一个具有适当的集合集.
更新:案例描述.
用例1:加载TableC的单个对象
Session session = (Session) getHibernateTemplate().getSessionFactory().openSession();
SQLQuery sqlQuery = session.createSQLQuery("SELECT c.* FROM TableC c WHERE c.ID_C = 1"); //Oracle
sqlQuery.addEntity("c", TableC.class);
return sqlQuery.list(); //Return list with sigle object of TableC
Run Code Online (Sandbox Code Playgroud)
在这种情况下一切正常.该对象加载了TableB对象列表中的所有数据和适当的项.在这个对象上,我们可以操作,更改它并更新数据库中的修改.
用例2加载对象集合
Session session = (Session) getHibernateTemplate().getSessionFactory().openSession();
SQLQuery sqlQuery = session.createSQLQuery("SELECT c.* FROM TableC c WHERE c.ID_C in (1,2)"); //Oracle
sqlQuery.addEntity("c", TableC.class);
return sqlQuery.list(); // throws "collection is not associated with any session"
Run Code Online (Sandbox Code Playgroud)
在这种情况下,Hibernate在检索下一个对象时抛出异常.
*代码只是样本,会话毕竟是关闭的.
经过一些研究,问题在于Hibernate,它被称为bug #HHH-2862
这主要是由于拥有一个急切的集合,其中集合的密钥在结果中不是唯一的.
当Hibernate使用'persistenceContext.addUninitializedCollection()'初始化集合时,这将检测到已经添加了具有给定键的集合,然后将旧实例设置为null并将当前设置为集合.但是,该集合已经通过较早的调用添加到持久化上下文中,并且当StatefulPersistenceContext.initializeNonLazyCollections()对持久化上下文中的所有集合进行迭代时forceInitialization(),对引用的调用会触发空引用,该引用将引发"集合与任何会话无关"异常"这解释了为什么在我的情况下只有最后一个对象有引用,只有一个一切都工作正常,你的假设关于延迟初始化问题.
一些想法如何绕过这个bug?
当你有一个引用非主键的外键时,你应该使用property-ref属性
它的文件
连接到此外键的关联类的属性名称
并且因为您的Collection 是不可变的,所以您应该将mutable属性设置为false
这是你的映射
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--Set up your package right here-->
<hibernate-mapping package="br.com.ar">
<class name="Aa" table="A">
<id name="id">
<generator class="native"/>
</id>
</class>
<class name="Bb" table="B">
<id name="id">
<generator class="native"/>
</id>
<many-to-one name="aa" column="A_ID" class="Aa"/>
</class>
<class name="Cc" table="C">
<id name="id">
<generator class="native"/>
</id>
<many-to-one name="aa" column="A_ID" class="Aa"/>
<bag name="bbList" table="B" mutable="false">
<key column="A_ID" property-ref="aa"/>
<one-to-many class="Bb"/>
</bag>
</class>
</hibernate-mapping>
Run Code Online (Sandbox Code Playgroud)
每个类描述如下
br.com.ar.Aa
public class Aa {
private Integer id;
// getter's and setter's
}
Run Code Online (Sandbox Code Playgroud)
br.com.ar.Bb
public class Bb {
private Integer id;
private Aa aa;
// getter's and setter's
}
Run Code Online (Sandbox Code Playgroud)
br.com.ar.Cc
public class Cc {
private Integer id;
private Aa aa;
private Collection<Bb> bbList = new ArrayList<Bb>();
// getter's and setter's
}
Run Code Online (Sandbox Code Playgroud)
添加为解决方法
好吧,让我们看看第一个用例
query = new StringBuilder().append("SELECT ")
.append("{cc.*} ")
.append("from ")
.append("C cc ")
.append("where ")
.append("cc.id = 1 ")
.toString();
Run Code Online (Sandbox Code Playgroud)
你说
该对象加载了所有数据及其Bb相关对象.在这个对象上,我们可以操作,更改它并更新数据库中的修改
如您所见,您的NATIVE SQL只检索Cc对象.没有加入.但是你说它检索所有相关的对象,包括bbList.如果是这样,它会发生,因为你有一个映射像(Notice fetch ="select"lazy ="false")
<class name="Cc" table="C">
...
<bag name="bbList" table="B" mutable="false" fetch="select" lazy="false">
<key column="A_ID" property-ref="aa"/>
<one-to-many class="Bb"/>
</bag>
</class>
Run Code Online (Sandbox Code Playgroud)
fetch ="select"使用额外的选择.对于一对多和多对多关系(bbList,右???),fetch属性与lazy属性一起使用,以确定加载相关集合的方式和时间.如果lazy ="true",则在应用程序访问集合时会加载集合,但如果lazy ="false",则Hibernate 会立即使用单独的SQL SELECT语句加载集合.
但如果跑了
query = new StringBuilder().append("SELECT ")
.append("{cc.*} ")
.append("from ")
.append("C cc ")
.append("where ")
.append("cc.id in (1,2) ")
.toString();
Run Code Online (Sandbox Code Playgroud)
我得到了预期
org.hibernate.HibernateException:collection与任何会话无关
为什么???
你想要加入Cc及其相关的bbList通过A_ID列暗示使用property-ref属性,对吗???
<class name="Cc" table="C">
...
<bag name="bbList" table="B" mutable="false" fetch="select" lazy="false">
<key column="A_ID" property-ref="aa"/>
<one-to-many class="Bb"/>
</bag>
</class>
Run Code Online (Sandbox Code Playgroud)
如果您想使用A_ID作为主键,则会发生这种情况,它必须是唯一的.因为有很多具有相同值的属性,您将获得此异常.第一个用例不会抛出任何异常,因为您刚刚检索了一个实体
解决方法
试着一个接一个
List<Cc> resultList = new ArrayList<Cc>();
for (Integer id : new Integer[] {1, 2}) {
query = new StringBuilder().append("SELECT ")
.append("{cc.*} ")
.append("from ")
.append("C cc ")
.append("where ")
.append("cc.id = :id ")
.toString();
resultList.addAll(
session.createSQLQuery(query)
.addEntity("cc", Cc.class)
.setParameter("id", id)
.list());
}
Run Code Online (Sandbox Code Playgroud)
或者使用普通的JDBC查询