无法使用JPA批注@OneToOne(cascade = CascadeType.ALL)

时间:2018-11-20 20:05:20

标签: java sql spring-boot sql-server-2012 spring-data-jpa

我有一个父类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);

如何使用合并和持久存储数据。

2 个答案:

答案 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()替换为主键获取器。

contactTypecontact.setContactType(contactTypeRepository.merge(contactType));合并到持久性上下文中也是有效的。