Spring Boot REST Api中的一对多关系

Posted

技术标签:

【中文标题】Spring Boot REST Api中的一对多关系【英文标题】:One to Many Relationship in spring boot REST Api 【发布时间】:2021-04-13 04:13:14 【问题描述】:

我正在使用 spring boot 来创建一个 REST API。在这个 API 中,我在 check-inGuests 之间建立了一对多的关系。我创建了一个控制器用于签入并使用 spring JPA 的保存功能。 保存方法是同时更新 checkinguest 表,但对于 guest 表,在 guest 表中签入外键 没有被添加,而是显示为 null。请有人帮助我。我需要同时创建来宾和签到。

签到模型

@Data
@Entity
public class Checkin 

    @Id
    private Long id;

    private Integer no_of_guests;

    @OneToMany(mappedBy = "checkin", cascade = CascadeType.ALL)
    private List<Guest> guests;

嘉宾模特

@Data
@Entity
public class Guest 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long guest_id;

    private String name;

    private String mobile_no;

    private String address;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "guest_checkin_id", nullable = false )
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private Checkin checkin;


签到控制器

@RestController
@RequestMapping("/checkin")
public class CheckinController 

    private final CheckinRepository checkinRepo;
    private final GuestRepository guestRepo;

    public CheckinController(CheckinRepository checkinRepo,GuestRepository guestRepo)
        this.checkinRepo = checkinRepo;
        this.guestRepo = guestRepo;
    

    @PostMapping("/add")
    ResponseEntity<Object> roomCheckin(@RequestBody Checkin checkin)
         if(checkinRepo.save(checkin) != null)
            return ResponseEntity.accepted().body("Checkin Successfull");
        
        return ResponseEntity.unprocessableEntity().body("Failed to Checkin");
    

【问题讨论】:

您是否尝试将 Cascade Persist 添加到一对多关系中? baeldung.com/jpa-cascade-types#2-cascadetypepersist CascadeType.ALL 已经包含“Cascade Persists”。将@GeneratedValue(strategy = GenerationType.IDENTITY) 添加到 Checkin 实体的 id 怎么样? 【参考方案1】:

使用实体类作为视图模型类可能有点棘手,尤其是在CheckinGuest 之间存在双向一对多关系的情况下。

让我们首先验证实体类和存储库是否按照描述的方式工作。为了使测试运行,我必须在类Checkin 中为id 字段添加@GeneratedValue

其他变化:

使用Set 代替List。见https://***.com/a/6563037/14072498 使用 @Getter@Setter 而不是 @Data 关系

我在下面添加了一个测试类CheckinRepositoryTest 以验证代码。


如前所述,使用实体类作为视图模型类可能会很棘手,因此下一步将引入两个新的视图模型类:CheckinVMGuestVM,以及一个新的服务类 GuestService负责保存CheckinGuest 实例。代码如下所示。

请注意,我只为GuestService 类添加了骨架,我将其留给您来实现。 CheckinRepositoryTest 里面的代码说明了如何实现。

有了视图模型类,@JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 应该被删除。


所有部分都启动并运行后,您应该为新的视图模型类添加验证,以及一些验证行为的集成测试。如果您遵循 TDD,您将在途中添加测试。

您还应该查看您的控制器方法,并为成功和失败情况使用适当的状态代码。


客座

package no.mycompany.myapp.misc;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;

@Getter
@Setter
@Entity
public class Guest 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long guest_id;

    private String name;

    private String mobile_no;

    private String address;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "guest_checkin_id", nullable = false)
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private Checkin checkin;

GuestVM 类

package no.mycompany.myapp.misc;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class GuestVM 

    private long id;
    private String name;
    private String mobile_no;
    private String address;

    public GuestVM(Guest guest) 
        this.id = guest.getGuest_id();
        this.name = guest.getName();
        this.mobile_no = guest.getMobile_no();
        this.address = guest.getAddress();
    

签到班

package no.mycompany.myapp.misc;

import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Getter
@Setter
@Entity
public class Checkin 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Integer no_of_guests;

    @OneToMany(mappedBy = "checkin", cascade = CascadeType.ALL)
    private Set<Guest> guests = new HashSet<>();

CheckinVM 类

package no.mycompany.myapp.misc;

import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.stream.Collectors;

@Getter
@Setter
@NoArgsConstructor
public class CheckinVM 

    private long id;
    private List<GuestVM> guests;

    public CheckinVM(Checkin checkin) 
        this.id = checkin.getId();
        this.guests = checkin.getGuests().stream().map(GuestVM::new).collect(Collectors.toList());
    

签入仓库

package no.mycompany.myapp.misc;

import org.springframework.data.jpa.repository.JpaRepository;

public interface CheckinRepository extends JpaRepository<Checkin, Long>  

CheckinRepositoryTest

package no.mycompany.myapp.misc;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

@DataJpaTest
public class CheckinRepositoryTest 

    @Autowired
    TestEntityManager testEntityManager;

    @Autowired
    CheckinRepository checkinRepository;

    @Test
    public void test() 

        // create instances
        var checkinInDb = new Checkin();
        var guestInDb = new Guest();

        // add relations
        guestInDb.setCheckin(checkinInDb);
        checkinInDb.getGuests().add(guestInDb);

        // save check-in
        checkinRepository.save(checkinInDb);

        // verify that check-in has one guest
        var checkin = testEntityManager.find(Checkin.class, checkinInDb.getId());
        assertThat(checkin.getGuests().size()).isEqualTo(1);

        // verify that guest is connected to a check-in
        var guest = testEntityManager.find(Guest.class, guestInDb.getGuest_id());
        assertThat(guest.getCheckin()).isNotNull();
    

CheckinService 类:我把这个留给你实现

package no.mycompany.myapp.misc;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class CheckinService 

    private final CheckinRepository checkinRepository;

    public CheckinVM saveCheckin(CheckinVM checkin) 
        return null; // TODO: implement this
    

控制器类

package no.mycompany.myapp.misc;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/checkin")
@RequiredArgsConstructor
public class CheckinController 

    private final CheckinService checkinService;

    @PostMapping("/add")
    ResponseEntity<Object> roomCheckin(@RequestBody CheckinVM checkin) 
        if (checkinService.saveCheckin(checkin) != null) 
            return ResponseEntity.accepted().body("Checkin Successful");
        
        return ResponseEntity.unprocessableEntity().body("Failed to Checkin");
    

【讨论】:

以上是关于Spring Boot REST Api中的一对多关系的主要内容,如果未能解决你的问题,请参考以下文章

使用带有 MySQL 数据库的 Spring Boot Rest API 的一对一映射

REST API 在带有 H2 数据库的 Spring Boot Maven 多模块项目中总是抛出 404 错误

Spring Boot REST JPA JSON 格式

如何在Spring Boot Rest API中的BeanUtils.copyProperties中将String转换为枚举

如何保护 Spring Boot Web 应用程序中的 REST API?

Spring Boot项目中的MongoDB一对多和多对一关系