Sping Boot JPA一对多关系-如果不存在,则创建相关对象

6bc51xsx  于 2022-11-14  发布在  其他
关注(0)|答案(1)|浏览(154)

假设我在PersonJob之间有一个一对多关系,每个人只有一个作业,而一个作业有很多人。
我有一个控制器,它调用服务,服务调用将执行查询的存储库。
它们是:

@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> {

}

现在是模型和我定义它们之间关系的方式。我可以用两种方式来编码。

第一章:

@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"
}

塞纳里奥2

如果我稍微改变关系注解中的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;

然后,当我传递 First 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"
}

但是,如果我自己在数据库中创建了工作记录,然后用 Second Json 执行请求,它将工作,并创建与现有工作记录有关系的人员。
现在我的问题是:
我如何将两者结合起来?我希望JPA两者都能做到。有没有什么方法,能够传递两个jsons,并且如果Id为空,JPA会自动创建作业,如果它有Id,则获取并重用它?

v8wbuo2f

v8wbuo2f1#

我找到了修复程序。删除个人实体的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
}

输出(在数据库人员表中):

作业表:

相关问题