[英]Spring boot JPA OneToMany relationship - create related object if not exist
假设我在Person
和Job
之间有 oneToMany 关系。 每个人只有一份工作,而一份工作有很多人。
我有一个 controller 调用服务,该服务调用将执行查询的存储库。
他们来了:
@RestController
@CrossOrigin()
@RequestMapping(path = "api/person")
public class PersonController {
private final PersonService personService;
@Autowired
public PersonController(PersonService personService) {
this.personService = personService;
}
@PostMapping
public Person storePerson(@RequestBody Person person) {
return this.personService.storePerson(person);
}
//...more code is also here
}
@Service
public class PersonService {
private final PersonRepository personRepository;
@Autowired
public PersonService(PersonRepository personRepository, CountryRepository countryRepository,
JobRepository jobRepository, RoleRepository roleRepository, HairColorRepository hairColorRepository) {
this.personRepository = personRepository;
}
public Person storePerson(Person person) {
return this.personRepository.save(person);
}
//...more code is also here
}
@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {
}
现在模型和我定义它们之间关系的方式。 我可以通过两种方式对此进行编码。
情景 1:
@Entity
@Table(name = "people")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = Job.class)
@JoinColumn(name = "job_id")
private Job job;
// ...getters and setters, constructors, toString(), etc are here
}
@Entity
@Table(name = "jobs")
public class Job {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@OneToMany(mappedBy = "job", orphanRemoval = true, cascade = CascadeType.ALL)
private Set<Person> persons;
// ...getters and setters, constructors, toString(), etc are here
}
我使用 postman 将记录插入此数据库。 我发送一个POST
请求,这是正文:
第一个 Json
{
"name": "James",
"job": {
"id": null,
"name": "Doctor"
}
}
这很完美,因为它创建了这个人,它还创建了一个在数据库中不存在的新工作,并且还创建了两者之间的关系。 但是根据第二个请求,我想重用该作业。 所以我提出这个要求:
第二个Json
{
"name": "David",
"job": {
"id": 1,
"name": "Doctor"
}
}
在这里我得到一个例外:
{
"timestamp": "2022-08-05T11:20:41.037+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "detached entity passed to persist: ir.arm.archiver.job.Job; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: ir.arm.archiver.job.Job",
"path": "/api/person"
}
Senario2
如果我稍微更改关系注释中的Cascade
值,我会得到完全相反的结果。 如果在Person.java
中,我将private Job job
字段的注释更改为使用Cascade.MERGE
,如下所示:
@ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.EAGER, targetEntity = Job.class)
@JoinColumn(name = "job_id")
private Job job;
然后,当我通过第一个 Json时,这一次,我得到一个异常:
{
"timestamp": "2022-08-05T11:36:17.854+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : ir.arm.archiver.person.Person.job -> ir.arm.archiver.job.Job; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : ir.arm.archiver.person.Person.job -> ir.arm.archiver.job.Job",
"path": "/api/person"
}
但是,如果我自己在数据库中创建工作记录,然后使用第二个 Json执行请求,它将起作用,并创建与现有工作记录有关系的人。
现在我的问题是:
我怎样才能将两者结合起来? 我希望 JPA 两者都做。 有什么办法可以同时传递 jsons 和 jpa 自动创建作业,如果Id
是 null,如果它有Id
则获取并重用它?
我找到了解决办法。 移除 Person Entity 的 Job 成员变量的cascade
属性。 JPA 将两者结合起来。
更新人员实体:
@Entity
@Table(name = "people")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.EAGER, targetEntity = Job.class)
@JoinColumn(name = "job_id")
private Job job;
// ...getters and setters, constructors, toString(), etc are here
}
Output(在数据库人员表中):
工作表:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.