Java 同一实体的多个表示正在合并JPA

Java 同一实体的多个表示正在合并JPA,java,hibernate,jpa,Java,Hibernate,Jpa,模式定义 CREATE TABLE person ( id bigint(20) NOT NULL AUTO_INCREMENT, PRIMARY KEY (id) ) ENGINE=InnoDB CREATE TABLE address ( person_id bigint(20) not null, postcode varchar(255) not null, state int(11), constraint `PRIMARY` primary

模式定义

CREATE TABLE person (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (id)
) ENGINE=InnoDB

CREATE TABLE address (
    person_id bigint(20) not null,
    postcode varchar(255) not null,
    state int(11),
    constraint `PRIMARY` primary key (address_id, postcode),
    constraint FK_dq8j32idfdnwny42fiovenqwo foreign key (person_id) references person (id)
) ENGINE=InnoDB
即只要邮政编码不同,一个人就可以有多个地址

班级

@Entity
@Getter
@Setter
class Person implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="id", unique = true, nullable = false)
    private Long id;

    @OneToMany(cascade= CascadeType.ALL)
    private Set<Address> Addresses = new HashSet<>();

    protected Person(){}
}

@Entity
@Getter
@Setter
class Address implements Serializable {
    @EmbeddedId
    private AddressId addressId;
    private State state;
    protected Address(){}
}

@Embeddable
@Getter
@Setter
public class AddressId implements Serializable {
    @Column(name = "person_id")
    private Long personId;
    @Column(name = "postcode")
    private String postcode;
}
但是当尝试更新其中一个时(例如更改状态)

下面是生成的。我认为下面的意思基本上是“嘿,你试图添加一个已经存在的带有个人id和邮政编码的地址,我不在乎国家是否不同,你不能这样做”。如何让它发挥作用

原因:java.lang.IllegalStateException:多个表示 同一实体[地址]的#AddressId@5047ce78]正在合并。 管理:[Address@5c220478]; 分离的:[Address@79804699]在 org.hibernate.event.internal.EntityCopyNotAllowedObserver.entityCopyDetected(EntityCopyNotAllowedObserver.java:51) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.event.internal.MergeContext.put(MergeContext.java:262) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:216) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:192) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:886) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:868) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:277) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:379) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:319) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:296) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.engine.internal.Cascade.Cascade(Cascade.java:118) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:474) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:218) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:192) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:85) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:876) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:858) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:863) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1196) ~[hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]at sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法) ~(na:1.8.0_102)at invoke(NativeMethodAccessorImpl.java:62) ~(na:1.8.0_102)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 位于java.lang.reflect.Method.invoke(Method.java:498)的~[na:1.8.0\u 102] ~[na:1.8.0_102]

然而,如果我这样做

Person person = personRepo.findOne(personId);
new ArrayList<>(person.getAddresses()).get(0).setState(State.QLD); //change state of existing address row
person = personRepo.save(person);
Person-Person=personRepo.findOne(personId);
新建ArrayList(person.getAddresses()).get(0.setState(State.QLD)//更改现有地址行的状态
人员=人员回购保存(人员);

我没有例外。我认为Hibernate检测到,在这种情况下,一个现有的行正在被更新,而如果试图添加另一个地址,该地址与已经存在的地址具有相同的personId和邮政编码,它会尝试进行插入,但失败。有没有办法让Hibernate也在date的情况下进行更新?

您正在使用相同的AddressId键创建一个新的Address对象

由于不重写equals和hashCode,因此会将新的重复地址插入到集合中,而不是替换旧的address对象

这将创建具有相同AddressId的地址的副本

因此,要么在地址中实现equals,以便集合替换重复,要么从集合中提取正确的地址并直接更改其状态

Address的equals和hashCode应该调用AddressId的equals和hashCode。addressId equald和hashCode应检查人名和邮政编码


使用intelliJ或eclipse等现代理念,您可以自动生成与postCode和personId相关的方法。您正在使用相同的AddressId键创建一个新的Address对象

由于不重写equals和hashCode,因此会将新的重复地址插入到集合中,而不是替换旧的address对象

这将创建具有相同AddressId的地址的副本

因此,要么在地址中实现equals,以便集合替换重复,o
Person person = personRepo.findOne(personId);

AddressId addressId = new AddressId();
addressId.setPersonId(personId);
addressId.setPostcode("4000"); //person already has an address with this postcode
Address address = new Address();
address.setAddressId(addressId);
address.setState(State.TAS); //but I want to change the state from QLD to TAS
person.getAddresses().add(address);
person = personRepo.save(person);
Person person = personRepo.findOne(personId);
new ArrayList<>(person.getAddresses()).get(0).setState(State.QLD); //change state of existing address row
person = personRepo.save(person);
Person person = personRepo.findOne(personId);

AddressId addressId = new AddressId();
addressId.setPersonId(personId);
addressId.setPostcode("4000"); //person already has an address with this postcode
Address address = null;
for (Address a : person.getAddresses()) {
    if (a.getAddressId().equals(addressId)) {
        address = a;
        break;
    }
}
if (address == null) {
    address = new Address();
    address.setAddressId(addressId);
    address.setState(State.TAS); //but I want to change the state from QLD to TAS
    person.getAddresses().add(address);
}
else {
    address.setState(State.TAS);
}
person = personRepo.save(person);
@Entity
@Getter
@Setter
class Person implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="id", unique = true, nullable = false)
    private Long id;

    @ElementCollection
    @CollectionTable(name = "address", joinColumns = @JoinColumn(name = "person_id"))
    private Set<Address> Addresses = new HashSet<>();

    protected Person(){}
}

@Embeddable
@Getter
@Setter
class Address implements Serializable {

    private State state;
    private String postcode;

    protected Address(){}

    // override equals and hascode based on post code only
    // this will give you the desired behaviour
}