JPA:如果孩子有嵌入式ID,如何级联保存实体?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JPA:如果孩子有嵌入式ID,如何级联保存实体?相关的知识,希望对你有一定的参考价值。
我有以下父实体。
@Entity
@Table(name = "vital_entry")
public class VitalEntry {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "vital_entry_id", columnDefinition = "INT")
private int vitalEntryId;
@OneToMany(mappedBy = "vitalEntry", cascade = CascadeType.ALL)
private Set<VitalEntryValues> vitalEntryValues = new HashSet<>();
.... other fields
}
这是我的孩子实体
@Entity
@Table(name = "vital_entry_values")
public class VitalEntryValues {
@EmbeddedId
private VitalEntryValuesId vevId;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@MapsId("vital_entry_id")
@JoinColumn(name = "vital_entry_id", columnDefinition = "INT")
private VitalEntry vitalEntry;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@MapsId("vital_id")
@JoinColumn(name = "vital_id", columnDefinition = "INT")
private Vitals vitals;
@Column(name = "vital_unit_id", insertable = false, updatable = false)
private int vitalUnitId;
@Basic
private double value;
@Basic
@Column(columnDefinition = "INT")
private int type;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@JoinColumn(name = "vital_unit_id", columnDefinition = "INT")
private VitalUnits vitalUnits;
public VitalEntryValues() {
}
public VitalEntryValues(double value, int type, VitalEntry vitalEntry, VitalUnits vitalUnits, Vitals vitals) {
this.value = value;
this.type = type;
this.vitalEntry = vitalEntry;
this.vitalUnits = vitalUnits;
this.vitals = vitals;
}
}
和EmbeddedId类:
@Embeddable
public class VitalEntryValuesId implements Serializable {
@Column(name = "vital_entry_id", columnDefinition = "INT", insertable = false, updatable = false)
private int vitalEntryId;
@Column(name = "vital_id", columnDefinition = "INT", insertable = false, updatable = false)
private int vitalId;
}
我就这样保存它:
VitalEntry = new VitalEntry(accountId, vitalEntryVO.getDateTime(),
DateUtility.getCurrentGMTDate(), vitalEntryVO.getNotes(), vitalEntryVO.getVitalEntryType(), vitalEntryVO.getParentVitalId());
Set<VitalEntryValues> vitalEntryValues = new HashSet<>();
for (VitalEntryValueVO value : vitalEntryVO.getValues()) {
VitalEntryValues values = new VitalEntryValues(value.getValue(), vitalEntryVO.getType(), vitalEntry,
vitalRepo.findUnit(value.getUnitId()), vitalRepo.findOne(value.getVitalId()));
vitalEntryValues.add(values);
}
vitalEntry.setVitalEntryValues(vitalEntryValues);
vitalEntry = vitalEntryRepo.save(vitalEntry);
但是在保存时我得到了这个例外:
Caused by: org.hibernate.id.IdentifierGenerationException: null id generated for:class com.v2.entity.VitalEntryValues
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:107)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:804)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:771)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:80)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:458)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:383)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:193)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:491)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:423)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:386)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:193)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:126)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:445)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:281)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:113)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:780)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:765)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
at com.sun.proxy.$Proxy555.persist(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:508)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:504)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:489)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
我在这做错了什么?任何帮助表示赞赏..
编辑:
@Entity
@Table(name = "vitals")
public class Vitals {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "vital_id", columnDefinition = "INT")
private int vitalId;
@Basic
@Column(name = "has_value")
private boolean hasValue;
@Basic
@Column(name = "is_decimal")
private boolean isDecimal;
@Basic
@Column(name = "is_oneday_reading")
private boolean isOnedayReading;
@Basic
@Column(length = 100)
private String name;
@Basic
@Column(name = "parent_id", columnDefinition = "INT")
private int parentId;
@OneToMany(targetEntity = PatientVitals.class, mappedBy = "vitals", cascade = CascadeType.MERGE)
private Set<PatientVitals> patientVitals = new HashSet<PatientVitals>();
@Basic
@Column(columnDefinition = "INT")
private int type;
@OneToMany(targetEntity = VitalEntryValues.class, mappedBy = "vitals", cascade = CascadeType.MERGE)
private Set<VitalEntryValues> vitalEntryValues = new HashSet<VitalEntryValues>();
@OneToMany(targetEntity = VitalUnits.class, mappedBy = "vitals", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
private Set<VitalUnits> vitalUnits = new HashSet<VitalUnits>();
}
答案
Introduction and context
我使用了提供的源代码的简化版本,足以重现所描述的问题。
因此,分析和解决方案可能不完整。
Hibernate版本:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.10.Final</version>
</dependency>
Analysis and solution
1. MapsId
注释用法
请考虑查看有关MapsId
注释及其用法的文档。
对于提供的上下文,它应该是:
...
public class VitalEntryValues {
...
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@MapsId("vitalEntryId")
@JoinColumn(name = "vital_entry_id", columnDefinition = "INT")
private VitalEntry vitalEntry;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@MapsId("vitalId")
@JoinColumn(name = "vital_id", columnDefinition = "INT")
private Vitals vitals;
...
}
2. Hibernate: EmbeddedId
field initialisation
正如我的实验所示,用EmbeddedId
注释注释的字段也需要初始化以避免内部Hibernate异常 - org.hibernate.PropertyAccessException
。有关详细信息,请参阅the question。
...
public class VitalEntryValues {
...
@EmbeddedId
private VitalEntryValuesId vevId = new VitalEntryValuesId();
...
}
希望这可以帮助。
以上是关于JPA:如果孩子有嵌入式ID,如何级联保存实体?的主要内容,如果未能解决你的问题,请参考以下文章