【问题标题】:@ElementCollection Java Persistence (Hibernate) Causes Loading of Duplicate Instances@ElementCollection Java 持久性(休眠)导致加载重复实例
【发布时间】:2011-10-08 19:22:50
【问题描述】:

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

例如,具有单个 MyClass 实例且 collectionOfStrings.size() == 4 的数据库,加载所有 MyClass 值的调用将返回大小为 4 的列表(所有相同的对象)而不是仅 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();

谢谢, 重型E

编辑:添加了 findByCriteria 调用。

【问题讨论】:

  • axtavt,我在我最初忽略的 findByCriteria 方法中添加了。

标签: java hibernate annotations persistence


【解决方案1】:

我不确定这是错误还是合法行为,但可以通过应用 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();
}

【讨论】:

  • 我也面临同样的问题,但在 2020 年,使用 JpaRepository,您知道如何解决它吗?
【解决方案2】:

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

请参阅 Hibernate ORM 问题跟踪器中的票证 HHH-6783 以了解此确切问题。显然没有解决方案。 :-(

此处还提供了一个链接to the Hibernate FAQ,该链接涉及外部联接问题。

我正在处理完全相同的问题。在我的情况下,使用 @ElementCollection 是有意义的,但不会以审查我所有的数据访问层实现为代价。 一个人要做什么?

【讨论】:

  • 它在 HHH-6783 中被标记为不是错误,但我闻起来很像错误。我使用 Hibernate.initialize() 使用 LAZY,因为在我的用例中,只返回了几个对象并且 N+1 查询都可以。
【解决方案3】:

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

这是 Hibernate 可以映射的通用集合类型:

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

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

List 是一个索引包。该索引让 hibernate 知道一个特定的内存对象是否与一个相等的数据库对象相同,因此不需要完全删除/重新插入。

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

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

【讨论】:

  • 不幸的是,使用 Set 不适用于此类 - List 是必需的,因为它是有序集合。为了澄清起见,问题不在于 List 'collectionOfStrings' 包含重复项,而是 List 'myClassInstances' 包含重复项。
猜你喜欢
  • 2012-10-07
  • 2012-07-22
  • 1970-01-01
  • 1970-01-01
  • 2021-03-18
  • 2011-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多