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

@ElementCollection Java持久性(Hibernate)导致加载重复实例,java,hibernate,annotations,persistence,Java,Hibernate,Annotations,Persistence,使用@ElementCollection时,load all正在加载一个对象的多个实例。更具体地说,它为collectionofstring中的每个元素加载一个实例 例如,如果一个数据库有一个MyClass实例,其collectionOfStrings.size()==4,则加载所有MyClass值的调用将返回一个大小为4的列表(所有相同的对象),而不仅仅是一个对象 是否有一个干净简单的方法来解决这个问题,或者是预期的行为 // Parent class is a @MappedSupercla

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

例如,如果一个数据库有一个MyClass实例,其collectionOfStrings.size()==4,则加载所有MyClass值的调用将返回一个大小为4的列表(所有相同的对象),而不仅仅是一个对象

是否有一个干净简单的方法来解决这个问题,或者是预期的行为

// 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();
//父类是@MappedSuperclass,它可能与问题相关,也可能与问题无关
@实体
公共类MyClass扩展了父类{
@身份证
@GeneratedValue(策略=GenerationType.AUTO)
私人长id;
@ElementCollection(fetch=FetchType.EAGER)
@IndexColumn(name=“IndexColumn”)
私有列表集合字符串;
//其他实例变量、构造函数、getter、setter、toString、hashcode和equals
}
公共类MyClassDAO_Hibernate扩展了GenerichBernateDAO实现了MyClassDAO{
@凌驾
公共列表loadAll(){
List entityList=null;
Session=getSession();
Transaction=session.beginTransaction();
entityList=findByCriteria(会话);
trans.commit();
返回实体列表;
}
}
受保护列表findByCriteria(会话、标准…标准){
Criteria crit=session.createCriteria(getPersistentClass());
对于(标准c:标准){
标准增补(c);
}
返回crit.list();
}
MyClassDAO MyClassDAO=新的MyClassDAO_Hibernate();//实际上,实现类型是由工厂确定的
...
List myClassInstances=myClassDAO.loadAll();
谢谢, 重量级


编辑:添加了findByCriteria调用。

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

这是可由Hibernate映射的常规集合类型:

Set是一个集合,其中的项不会出现一次以上。根据我的经验,这是最常见的持久集合类型

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

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

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


因此,在您的情况下,我建议您使用Set。

我不确定这是错误还是合法行为,但可以通过应用
DISTINCT\u ROOT\u 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();
}
受保护列表findByCriteria(会话、标准…标准){
Criteria crit=session.createCriteria(getPersistentClass());
对于(标准c:标准){
标准增补(c);
}
crit.setResultTransformer(Criteria.DISTINCT\u ROOT\u实体);
返回crit.list();
}

只有在急切地获取集合时才会观察到这一点。Hibernate将此注释映射转换为外部联接查询,这会导致每个链接的collectionOfString元素在根元素列表中出现一个倍数

请参阅Hibernate ORM问题跟踪器中的票证以了解这个确切的问题。显然没有解决办法-(

这里还提供了一个关于外部联接问题的链接

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

不幸的是,使用集合对此类不起作用-需要列表,因为它是一个有序集合。为了澄清,问题不在于列表“collectionOfStrings”包含重复项,而在于列表“myClassInstances”包含重复项。axtavt,我在我的findByCriteria方法中添加了我忽略了包含的最初是de。在HHH-6783中,它被标记为不是一个bug,但对我来说,它闻起来很像一个bug。我使用Hibernate.initialize()进行懒惰,因为在我的用例中,只返回很少的对象,N+1查询是可以的。我面临着同样的问题,但在2020年,使用JpaRepository,你知道如何解决它吗?