代码之家  ›  专栏  ›  技术社区  ›  Damian Leszczyński - Vash

不相关集合的休眠映射问题

  •  3
  • Damian Leszczyński - Vash  · 技术社区  · 16 年前

    欢迎,

    我对休眠映射有一些问题。

    数据库结构:

    TableA
     -ID_A --PK
    
    TableB
     -ID_B --PK
     -ID_A -- FK -> TableA
    
    TableC
     -ID_C -- PK
     -ID_A -- FK -> TableA
    

    POJO结构:

    class TableA extends Pojo {
    
     /*Some Fields*/
    
    }
    
    class TableB extends Pojo {
    
      TableA tableA; 
    
     /*Some properties*/
    
    }
    
    class TableC extends Pojo {
    
     TableA tableA;
    
     Collection<tableB> tableBs;
    
    }
    

    我想拥有的是tablec 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
    

    在这种情况下,一切正常。对象将加载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" 
    

    在这种情况下,Hibernate在检索下一个对象时引发异常。

    *这些代码只是示例,会话毕竟是关闭的。

    4 回复  |  直到 16 年前
        1
  •  9
  •   Damian Leszczyński - Vash    16 年前

    经过一些研究,问题出在冬眠中,这就是所谓的虫子。 #HHH-2862

    这主要是由于有一个热切的集合,其中集合的键在结果中不是唯一的。

    当Hibernate使用“PersistenceContext.AddUninitializedCollection()”初始化集合时,这将检测到已添加具有给定键的集合,然后将旧实例设置为空,并将集合设置为当前实例。但是,该集合已通过早期调用添加到持久性上下文中,并且 StatefulPersistenceContext.initializeNonLazyCollections() 循环访问持久上下文调用中的所有集合 forceInitialization() 在引用中命中空引用,该引用引发“集合未与任何会话关联”异常。这就解释了为什么在我的例子中,只有最后一个对象有引用,并且只有一个对象,所有的东西都运行良好,以及您关于延迟初始化问题的假设。

    有没有想过如何绕过这个bug?

        2
  •  5
  •   Arthur Ronald    16 年前

    当您有一个引用非主键的外键时,应该使用property ref属性

    其文档

    关联类的属性的名称 与此外键联接的

    因为你的收藏 是不可变的 应将可变属性设置为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>
    

    每一类的描述如下

    B.C.A.A.AA

    public class Aa {
    
        private Integer id;
    
        // getter's and setter's
    
    }
    

    B.CO.B.B.B.

    public class Bb {
    
        private Integer id;
    
        private Aa aa;
    
        // getter's and setter's
    
    }
    

    英国皇家海军陆战队

    public class Cc {
    
        private Integer id;
    
        private Aa aa;
    
        private Collection<Bb> bbList = new ArrayList<Bb>();
    
        // getter's and setter's
    
    }
    

    作为解决方案添加

    好吧,让我们看看 第一用例

    query = new StringBuilder().append("SELECT ")
                                   .append("{cc.*} ")
                               .append("from ")
                                   .append("C cc ")
                               .append("where ")
                                   .append("cc.id = 1 ")
                               .toString();
    

    你说

    客体 加载所有数据及其与bb相关的对象 . 在这个物体上我们可以操作, 更改并更新数据库中的修改

    如你所见,你的 本地人 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>
    

    取回=“选择” 使用其他选择。对于一对多和多对多的关系(bblist,对吧???)fetch属性 协同工作 使用lazy属性来确定加载相关集合的方式和时间。如果lazy=“true”,则应用程序访问集合时将加载集合,但如果lazy=“false”,则休眠 立即加载集合 使用单独的SQL select语句。

    但是如果运行

    query = new StringBuilder().append("SELECT ")
                                   .append("{cc.*} ")
                               .append("from ")
                                   .append("C cc ")
                               .append("where ")
                                   .append("cc.id in (1,2) ")
                               .toString();
    

    我得到了期望

    org.hibernate.hibernateexception:集合未与任何会话关联

    为什么????

    你想通过一个专栏加入CC及其相关的bblist 这意味着使用属性ref属性 对吧????

    <class name=“c c”table=“c”>
    …
    <bag name=“bblist”table=“b”mutable=“false”fetch=“select”lazy=“false”>
    <key column=“a_id”property ref=“a a”/>
    <一对多class=“bb”/>
    和/袋;
    & lt;/类& gt;
    

    如果你想用一个\u id作为主键, 它一定是独一无二的 . 因为有很多AA属性 具有相同的值 ,您将得到这个异常。第一个用例不会引发任何异常,因为您刚刚检索到 实体

    解决办法

    试着一个接一个

    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());
    }
    

    或者使用普通的JDBC查询

        3
  •  0
  •   hvgotcodes    16 年前

    您可能在发布的代码中犯了错误,但是假设它是正确的,但是您的Tablec类有一个TableB列表,而您的Tablec数据库表有一个对表A的FK引用。

        4
  •  0
  •   Stefan Steinegger    16 年前

    imho,您没有按预期使用ORM。类模型与表结构完全相同。这种情况未必如此。这样,您就不会从ORM中受益。

    因此,我建议将b和c的列表放在表A中。然后,您可以在需要的地方导航:

    class A
    {
     IList<B> Bs { get; private set; }
     IList<C> Cs { get; private set; }
    }
    
    class B
    {
      A A { get; set; }
    }
    
    class C
    {
      A A { get; set; }
    }
    

    你把它画成这样:

    <class name="A">
      <bag name="As" class="A">
        <key column="ID_A"/>
      </bag>
      <bag name="Bs" class="B">
        <key column="ID_A"/>
      </bag>
    </class>
    
    <class name="B">
      <many-to-one name="A" class="A" column="ID_A"/>
    </class>
    
    <class name="C">
      <many-to-one name="A" class="A" column="ID_A"/>
    </class>
    

    然后您可以简单地按如下方式导航:

    B b = session.Get<B>(id);
    foreach(C c in b.A.Cs)
    {
      // iterate C's
    }
    

    HQL查询:

    SQLQuery sqlQuery = session.createQuery("FROM TableC c WHERE c.ID_C in (1,2)"); 
    
    • 我不知道您为什么在这里使用本机SQL。
    • 附录是增加 参数 查询类型实体(实际被视为其ID)。据我所见,查询中没有任何参数。

    编辑:

    查询以获取与某个C共享相同A的所有B:

        C c = session.Get<C>(cId);
    
        SQLQuery sqlQuery = session.createQuery(
    @"select B
    from B, C
    where B.A = C.A
      and C = :c")
          .addEntity("c", c);
    

    或更短:

        C c = session.Get<C>(cId);
    
        SQLQuery sqlQuery = session.createQuery(
    @"select B
    from B
    where B.A = :a")
          .addEntity("a", c.A);