问题 @ElementCollection Java持久性(Hibernate)导致加载重复实例


使用@ElementCollection时,load all正在加载对象的多个实例。更具体地说,它为collectionOfStrings中的每个元素加载一个实例。

例如,具有myClass的单​​个实例且具有collectionOfStrings.size()== 4的数据库,对加载所有MyClass值的调用将返回大小为4的List(所有相同的对象),而不是仅返回1个对象。

有没有一种干净简单的方法来解决这个问题,还是预期的行为?

// Parent class is a @MappedSuperclass which may or may not be relevant to the issue
@Entity
public class MyClass extends ParentClass {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @ElementCollection(fetch=FetchType.EAGER)
    @IndexColumn(name="indexColumn")
    private List<String> collectionOfStrings;

    // other instance variables, constructors, getters, setters, toString, hashcode and equals
}

public class MyClassDAO_Hibernate extends GenericHibernateDAO<MyClass, Long> implements MyClassDAO {

    @Override
    public List<MyClass> loadAll() {
        List<MyClass> entityList = null;
        Session session = getSession();
        Transaction trans = session.beginTransaction();
        entityList = findByCriteria(session);
        trans.commit();
        return entityList;
    }

}

protected List<T> findByCriteria(Session session, Criterion... criterion) {
    Criteria crit = session.createCriteria(getPersistentClass());
    for (Criterion c : criterion) {
        crit.add(c);
    }
    return crit.list();
}

MyClassDAO myClassDAO = new MyClassDAO_Hibernate(); // in reality, implementation type is determined with a Factory
...
List<MyClass> myClassInstances = myClassDAO.loadAll();

谢谢, HeavyE

编辑:添加了findByCriteria调用。


8693
2017-07-19 18:46


起源

你如何加载实体? - axtavt
axtavt,我添加了我的findByCriteria方法,我忽略了原来的方法。 - HeavyE


答案:


我不确定这是一个错误还是合法行为,但可以通过申请来解决 DISTINCT_ROOT_ENTITY 结果变换器:

protected List<T> findByCriteria(Session session, Criterion... criterion) {
    Criteria crit = session.createCriteria(getPersistentClass());
    for (Criterion c : criterion) {
        crit.add(c);
    }
    crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
    return crit.list();
}

8
2017-07-19 20:07



工作得很完美 - 谢谢! - HeavyE


只有在急切地获取集合时才会观察到这种情况。 Hibernate将此批注映射转换为外连接查询,这会在每个链接的collectionOfString元素的根元素列表中生成多个。

看票 HHH-6783 在Hibernate ORM问题跟踪器中针对此确切问题。显然没有解决方案。 :-(

一条链接 到Hibernate FAQ 这里也提供了与外连接有关的问题。

我正在处理完全相同的问题。在我的案例中使用@ElementCollection是有意义的,但不是以审查我的所有数据访问层实现为代价。 怎么办?


3
2018-02-10 22:23



它被标记为HHH-6783中的一个错误,但它闻起来就像一个臭虫。我使用Hibernate.initialize()来使用LAZY,因为在我的用例中只返回了几个对象,并且N + 1个查询都可以。 - Alex Pakka


这是List的正确行为。 List允许重复对象,这就是您需要索引列的原因。

这是可以通过Hibernate映射的常规集合类型:

 是一个集合,其中没有项目出现多次。根据我的经验,这是最常见的持久性集合类型。

 项目可能不止一次出现的集合:它们的效率非常低,因为hibernate无法判断您放入的项目是否与已经存在的项目相同(假设它们相同),因此它必须删除整个集合并从内存中重新保存。

名单 是一个索引袋。索引让hibernate知道特定的内存中对象是否与相同的on-DB对象相同,因此不需要完全删除/重新插入。

地图 就像一个列表,除了索引不必是可计算的(通常是顺序的)整数,它可以是任何东西,甚至是另一个对象。

所以在你的情况下,我建议你使用Set。


2
2017-07-19 19:09



不幸的是,使用Set不适用于此类 - 因为它是有序集合所以需要List。为了澄清,问题不在于List的collectionOfStrings'包含重复项,而是List'myClassInstances'包含重复项。 - HeavyE