尝试更新Hibernate实体会导致NonUniqueObjectionException异常



我有一个很难诊断的问题。我正在抓取一个网站的信息,作为该信息的一部分,我有一个PersonEntity,其中包含一组种族实体(这两个实体都有自己的表)。相关代码如下:

PersonEntity

    @Cascade(CascadeType.ALL)
    @OneToMany(targetEntity = EthnicityEntity.class, mappedBy = "ethnicityId.person", fetch = FetchType.LAZY)
    private Set<EthnicityEntity> ethnicities;

public EthnicityEntity addEthnicity(EthnicityEntity ethnicityEntity)
    {
        getEthnicities().add(ethnicityEntity);
        return ethnicityEntity;
    }
    public Set<EthnicityEntity> getEthnicities()
    {
        if (ethnicities == null)
        {
            ethnicities = new HashSet<EthnicityEntity>();
        }
        return ethnicities;
    }

EthnicityEntity

@Entity
@Table(name = "ETHNICITIES")
public class EthnicityEntity implements Serializable
{
    private static final long serialVersionUID = 5459112416842302917L;
    @EmbeddedId
    private EthnicityId ethnicityId;
    // setters, getters, equals, hashcode
}

EthnicityId

@Embeddable
public class EthnicityId implements Serializable
{
    private static final long serialVersionUID = -3596578582428917188L;
    @ManyToOne
    @Cascade(CascadeType.ALL)
    @Index(name = "ETHNICITY_PERSON_ID_INDEX")
    @JoinColumn(name = "PERSON_ID")
    @ForeignKey(name = "FKE1_PERSON_ID")
    private PersonEntity person;
    @Enumerated(EnumType.STRING)
    @Column(name = "ETHNICITY", length = 40, nullable = false)
    private Ethnicity ethnicity;
    // getters, setters, equals, hashcode
}

PersonDaoHibernate

@Transactional
public String update(PersonEntity person)
{
    if (person == null)
    {
        throw new IllegalArgumentException("Person entity cannot be null.");
    }
    getHibernateTemplate().update(person);
    // This is where it fails ^^
    return person.getPersonId();
}

我也有一些代码刮擦一个页面,从一个人的个人资料中获得这些数据:

private void findEthnicities(PersonEntity person, String userName, Document document)
    {
        String ethnicities = findElement(userName, document, "dd[id=ethnicities").toLowerCase();    
        for(Ethnicity e : Ethnicity.values())
        {
            if(ethnicities.contains(e.getString().toLowerCase()))
            {
                EthnicityEntity ethnicity = new EthnicityEntity(new EthnicityId(person, e));
                person.addEthnicity(ethnicity);
            }
        }
    }

目前我正在做的是通过他们的年龄,并试图更新我所有的当前用户(所以我首先从我的数据库中获得的人,然后更新任何相关的字段已经改变)。但是,当我到达某个用户时,我得到一个错误:

Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.anonymous.model.EthnicityEntity#com.anonymous.model.EthnicityId@ca737afe]
    at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:638)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:305)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:246)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:112)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
    at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:685)
    at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:677)
    at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:252)
    at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392)
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335)
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
    at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:425)
    at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:362)
    at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:338)
    at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
    at org.hibernate.engine.Cascade.cascade(Cascade.java:161)
    at org.hibernate.engine.Cascade.cascade(Cascade.java:127)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.cascadeOnUpdate(DefaultSaveOrUpdateEventListener.java:376)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:350)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:246)
    at org.hibernate.event.def.DefaultUpdateEventListener.performSaveOrUpdate(DefaultUpdateEventListener.java:57)
    at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
    at org.hibernate.impl.SessionImpl.fireUpdate(SessionImpl.java:742)
    at org.hibernate.impl.SessionImpl.update(SessionImpl.java:730)
    at org.hibernate.impl.SessionImpl.update(SessionImpl.java:722)

通过一些调试,我看到种族设置实际上有两个值,但是每个种族实体都有相同的值——'USERNAME'代表个人,'WHITE'代表种族。它们是一样的。我的第一个问题是,如果这是一个集合,怎么会是这样呢?我想这可能与我将一个未附加的实体(从抓取中获取的实体)添加到来自检索人员(附加实体)的集合有关,如果术语正确的话。然而,同样的代码适用于数百个具有相同种族的其他人(即"other - username"one_answers"WHITE")。此外,"WHITE"的实际值是从Enum中获取的,因此没有空格或沿着这些行创建一个看起来相同但不相同的值的问题。

这是让我抓狂的部分,如果我只为这个用户运行代码,甚至更令人困惑的是,即使我在大约30个用户中运行它。只有当它成为更长期运行的一部分时,才会发生这种情况。这也没有意义,因为这是我为更新多个用户所做的一切:

   public void crawlCurrentUsers(int lowAge, int highAge) {
            if (highAge < lowAge) {
                return;
            }
            for (int i = lowAge; i <= highAge; i++) {
                List<String> personIds = personDao.getDistinctPersonIds(i);
                for (String personId : personIds) {
                    crawlUser(personId, true, true);
                }
            }
        }

同样,如果我只对用户运行crawlUser,它可以工作。如果我以某种方式将抓取的用户限制为30个,就可以了。在Hibernate中是否存在某种类型的缓存可能不同步或类似的问题?我真的很困惑。

额外的调试表明,对于只有一个种族的所有其他用户,在findethniities方法中,在person. addethniity()之后,大小仍然是1,正如预期的那样(因为我只是将相同的值添加到集合中)。然而,当我到达这个问题用户时,大小实际上变成了2(但只有当我运行crawlCurrentUsers具有足够长的范围时)。我只是不知道该怎么做,想知道是否有人有一些建议或一些途径来尝试和探索。

这里的问题可能是您没有定义使用EthnicityEntity id字段的hashCodeequals方法。任何进入Set的东西显然都需要这些方法。它们存在吗?看起来你想把EthnicityEntity中的那些方法委托给EthnicityId中的相同方法,然后在那里定义它们。

我对EthnicityId里面的@ManyToOne Person有点困惑,这是EthnicityEntity@EmbeddableId。对我来说,这似乎是一个过于令人困惑的图式。虽然我认为缺少方法是问题所在,但您可能想看看是否可以简化对象层次结构或在对象上使用更简单的生成id和替代索引。

问题是我正在抓取的网站允许用户更改用户名。因为我是在抓取网站内容,所以我认为我必须让他们算作新人,因为没有办法知道谁曾经是谁。但是,我从调试中了解到,指向旧配置文件名称的链接会重定向到新的配置文件名称。不幸的是,我做了一个可能很糟糕的选择,即使用用户名作为一个人的主id,所以当在ethethyid的hashcode/equals中,它将尝试为USERNAME1进行更新,同时为USERNAME2提供个人id值。

这解释了为什么集合会添加两个看起来相同的值(它们是,但是等号/hashcode返回false)。它还解释了为什么运行crawlUser本身可以工作,因为我不知道,我使用的是重命名的人的用户名,而不是原来的人。如果我用原来的人来尝试它,它会失败。

呸,这太痛苦了!我想当我不能控制我收集的数据时,这肯定会发生。

相关内容

  • 没有找到相关文章

最新更新