JPA一对多关系创建实体两次

JPA one-to-many relationship creates entity twice

我正在使用 JPA 来保持双向一对多关系,一个人可以有多个地址。我故意不使用任何级联,我想自己存储每个实体。

在我的具体用例中,我有一个现有的个人实体并想添加一个地址。这发生在一个 facelet 中,用户不仅可以添加地址,还可以更改现有人员的属性。因此,在服务层中,我首先对人调用 em.merge(),然后对地址调用 em.persist()。问题是,该地址将被保留两次。

在调试时我发现,那个人的 em.merge() 不仅更新了这个人而且创建了地址,随后通过 em.persist().

再次创建了地址

在我的项目中,我在 Glassfish 服务器(使用 eclipselink)上使用 JSF、CDI 和 EJB,但我可以使用 RESOURCE_LOCAL JPA 在单元测试中重现该行为。附加的示例代码产生了我在 JavaEE 环境中遇到的确切错误。我做了以下观察:

问题是:为什么在一对一实体上调用 merge() 时创建了对多实体,尽管我没有指定任何级联?如果有人给我提示,我将不胜感激。

@Test
public void test_OK_OnlyPersistAddressDirect()
{
    // use case based on a existing person entity
    em.getTransaction().begin();
    Person p = new Person();
    p.setId(1L);
    p.setName("tom");
    p.setAddresses(new ArrayList<Address>());
    em.persist(p);
    em.getTransaction().commit();
    em.clear();

    // 1) load the existing entity
    Person persistedPerson = findPerson(1L);
    persistedPerson.setName("lisa");

    // 2) create a new address and link the two entities together
    Address a = new Address();
    a.setCity("paris");
    a.setPerson(persistedPerson);
    persistedPerson.getAddresses().add(a);

    // 3) do a merge on the existing person as its attributes might have changed and its list of addresses has enlarged
    em.getTransaction().begin();
    em.merge(persistedPerson);
    em.getTransaction().commit();

    // 4) do a persist on the address as it should be persisted in the database
    em.getTransaction().begin();
    em.persist(a);
    em.getTransaction().commit();

    // 5) check whether the person entity has changed
    List<Person> persons = em.createQuery("select p from Person p", Person.class).getResultList();
    Assert.assertEquals(1, persons.size());
    Assert.assertEquals("lisa", persons.get(0).getName());
    Assert.assertEquals(1, persons.get(0).getAddresses().size());

    // 6) check whether the address has been persisted
    List<Address> addresses = em.createQuery("select a from Address a", Address.class).getResultList();
    // HERE IS THE ERROR: expected:<1> but was:<2>
    Assert.assertEquals(1, addresses.size());
}
@Entity
public class Person implements Serializable
{
    @OneToMany(mappedBy = "person")
    private List<Address> addresses;
    ...
}
@Entity
public class Address implements Serializable
{
    @ManyToOne
    @JoinColumn(name = "PERSON_ID", nullable = false)
    private Person person;
    ...
}

我使用 Hibernate 作为 JPA 提供程序得到了正确的响应。由于您提到您正在 TopLink 下进行测试,我建议您向 TopLink 提交错误报告。

Hibernate: insert into Person (name, id) values (?, ?)
Hibernate: select person0_.id as id1_1_0_, person0_.name as name2_1_0_ from     Person person0_ where person0_.id=?
Hibernate: update Person set name=? where id=?
Hibernate: insert into Address (id, city, PERSON_ID) values (default, ?, ?)
Hibernate: select person0_.id as id1_1_, person0_.name as name2_1_ from Person person0_
[model.Person@133b7b68]
Hibernate: select address0_.id as id1_0_, address0_.city as city2_0_, address0_.PERSON_ID as PERSON_I3_0_ from Address address0_
-->> output of addresses.size() = 1

我没有尝试在 toplink 下重现它,我假设你的错误在那个配置下是可以重现的。