One to Many Relationship in spring boot REST Api

隐身守侯 提交于 2021-01-29 10:32:13

问题


I am using spring boot to create a REST API. In this API, I have a One to Many relationship between check-in and Guests. I created a controller for check-in and use that save function of spring JPA. The save method is updating both checkin and guest tables but for the guest table, the check-in foreign key in the guests table is not getting added instead showing as null. Please someone help me. I need to create both guests and checkin simultaneously.

Check-in Model

@Data
@Entity
public class Checkin {

    @Id
    private Long id;

    private Integer no_of_guests;

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

Guests Model

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

}

Check-in Controller

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

回答1:


Using entity classes as view model classes may be a little bit tricky, especially in this case when there is a bi-directional one-to-many relation between Checkin and Guest.

Let's start with verifying that the entity classes and repository is working as depicted. In order to make test run, I had to add @GeneratedValue for id field in class Checkin.

Other changes:

  • Use Set instead of List. See https://stackoverflow.com/a/6563037/14072498
  • Use @Getter and @Setter instead of @Data for entity classes with relations

I've added a test class CheckinRepositoryTest below in order to verify the code.


As mentioned earlier, using entity classes as view model classes can be tricky, hence next step will be to introduce two new view model classes: CheckinVM and GuestVM, and a new service class GuestService that will be responsible for saving Checkin with Guest instances. Code is shown below.

Please note that I added just the skeleton for GuestService class, I leave it to you to implement it. Code inside CheckinRepositoryTest indicates how to implement it.

With view model classes in place, @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) should be removed.


With all pieces up and running, you should add validation to your new view model classes, and some integration tests that verifies the behaviour. If you are following TDD, you'll add the tests on your way.

You should also have a look at your controller method and use appropriate status codes for success and failure situations.


Guest class

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 class

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();
    }
}

Checkin class

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 class

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());
    }
}

Checkin repository

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 class: I leave this to you to implement

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

Controller class

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");
    }
}


来源:https://stackoverflow.com/questions/65612311/one-to-many-relationship-in-spring-boot-rest-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!