簡體   English   中英

Spring 啟動 JPA OneToMany 關系 - 如果不存在則創建相關 object

[英]Spring boot JPA OneToMany relationship - create related object if not exist

假設我在PersonJob之間有 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM