我有一个父类Contact,它与ContactType具有一对一的关系。
@OneToOne(cascade=CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "type")
private ContactType contactType;
我正在尝试使用扩展JPARepository的ContactRepository.save()创建一个新联系人。我收到以下错误。
违反PRIMARY KEY约束类型。无法在对象ContactType中插入重复的键
如果我将联系人类型声明更改为以下内容:
@OneToOne(cascade=CascadeType.MERGE, orphanRemoval = true)
@JoinColumn(name = "type")
private ContactType contactType;
我收到以下错误:
对象引用了一个未保存的瞬态实例-在刷新之前保存该瞬态实例
用于创建联系人的代码(它仅调用JPARepository.save()):
contactsRepository.save(contact);
如何使用合并和持久存储数据。
答案 0 :(得分:0)
您提到的评论:
主键是字符串类型。我想插入新行以键入 用户在创建新联系人时输入新类型,例如:“ Type2”。如果 用户输入已经存在的类型,那么我不想插入新的 行。
那么这很有意义,为什么您会收到“无法插入重复的密钥”错误。
现实情况是,联系人和联系人类型之间没有一对一的关系,因为每个联系人都没有一种联系人类型。如您所说,联系人类型被重用。您应该做的是在“联系人”和“联系人类型”之间使用多对一的关系,因为一个联系人只能有一个“联系人类型”,但是一个“联系人类型”可以应用于多个联系人。现在,相同的联系人类型可以是一个以上的联系人。
因此,将其设为多对一,然后在保存之前,需要查找与给定联系人类型匹配的联系人类型,如果存在则插入该联系人类型;如果不存在,则填充该联系人类型,然后让级联保存新的联系人类型联系人类型。
答案 1 :(得分:0)
通过以下方式呼叫contactsRepository.save(contact)
:
@OneToOne(cascade=CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "type")
private ContactType contactType;
引发异常是因为持久性上下文将persist
操作级联并且将contactType
视为具有主键集且准备持久的transient
。具有相同PK的行已经存在,因此会出现错误。
第二种情况:
@OneToOne(cascade=CascadeType.MERGE, orphanRemoval = true)
@JoinColumn(name = "type")
private ContactType contactType;
该操作为persist
(不是merge
),因此未级联。持久性上下文将contactType
视为transient
,无法继续使用persist
,因为其中一个依赖项处于transient
状态。
解决方案
摆脱级联:
@OneToOne
@JoinColumn(name = "type")
private ContactType contactType;
在调用contactsRepository.save(contact);
之前,请确保contactType
处于托管状态。您可以这样操作:
contact.setContactType( entityManager.getReference(ContactType.class, contactType.getId()));
确保将getId()
替换为主键获取器。
将contactType
与contact.setContactType(contactTypeRepository.merge(contactType));
合并到持久性上下文中也是有效的。