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
}