Spring JPA 저장소의 여러 테이블 결과를 결합하는 방법
저는 Spring에 처음 와서 결과를 반환하기 위해 여러 테이블을 결합하는 방법을 모르겠습니다.저는 다음과 같은 작은 라이브러리 애플리케이션을 구현하려고 했습니다.
마이 엔티티 클래스 - 도서, 고객, 예약
Book.java - 도서관에서 구할 수 있는 책
@Entity
@Table(name = "books")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", columnDefinition = "int")
private int id;
@NotNull(message = "Book name cannot be null")
@Column(name = "book_name", columnDefinition = "VARCHAR(255)")
private String bookName;
@Column(name = "author", columnDefinition = "VARCHAR(255)")
private String author;
// getters and setters
public Book() {}
public Book(String bookName, String author) {
this.bookName = bookName;
this.author = author;
}
}
Customer.java - 라이브러리에 등록된 고객
@Entity
@Table(name = "customer", uniqueConstraints = {@UniqueConstraint(columnNames = {"phone"})})
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", columnDefinition = "int")
private int id;
@NotNull(message = "Customer name cannot be null")
@Column(name = "name", columnDefinition = "VARCHAR(255)")
private String name;
@Column(name = "phone", columnDefinition = "VARCHAR(15)")
private String phone;
@Column(name = "registered", columnDefinition = "DATETIME")
private String registered;
// getters and setters
public Customer() {}
public Customer(String name, String phone, String registered) {
this.name = name;
this.phone = phone;
this.registered = registered;
}
}
Booking.java - 고객의 모든 예약
@Entity
@Table(name = "bookings")
public class Booking {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", columnDefinition = "int")
private int id;
@NotNull(message = "Book id cannot be null")
@Column(name = "book_id", columnDefinition = "int")
private int bookId;
@NotNull(message = "Customer id cannot be null")
@Column(name = "customer_id", columnDefinition = "int")
private int customerId;
@Column(name = "issue_date", columnDefinition = "DATETIME")
private String issueDate;
@Column(name = "return_date", columnDefinition = "DATETIME")
private String returnDate;
// getters and setters
public Booking() {}
public Booking(int bookId, int customerId, String issueDate) {
this.bookId = bookId;
this.customerId = customerId;
this.issueDate = issueDate;
}
}
각 엔티티의 테이블 스키마는 다음과 같습니다.
서적:+-----------+--------------+------+-----+---------+----------------+| 필드 | 유형 | 특수한 순서 | 키 | 기본값 | 추가 |+-----------+--------------+------+-----+---------+----------------+| id | int(11) | NO | PRI | NULL | auto_increment || book_name | varchar (255) | NO | NULL | | || 작성자 | varchar (255) | YES | | NULL ||+-----------+--------------+------+-----+---------+----------------+id - 프라이머리 키 고객: +------------+--------------+------+-----+-------------------+-------------------+| 필드 | 유형 | 특수한 순서 | 키 | 기본값 | 추가 |+------------+--------------+------+-----+-------------------+-------------------+| id | int(11) | NO | PRI | NULL | auto_increment || name | varchar (255) | NO | | NULL | || registered | datetime | YES | | CURRENT_TIMESTamp | DEFAULT_GERATED || phone | varchar (15) | YES | UNI | NULL ||+------------+--------------+------+-----+-------------------+-------------------+id - 프라이머리 키 예약:+-------------+----------+------+-----+-------------------+-------------------+| 필드 | 유형 | 특수한 순서 | 키 | 기본값 | 추가 |+-------------+----------+------+-----+-------------------+-------------------+| id | int(11) | NO | PRI | NULL | auto_increment || book_id | int(11) | NO | MUL | 특수 ||| customer_id | int(11) | NO | MUL | NULL | || issue_date | datetime | YES | | CURRENT_TIMESTamp | DEFAULT_GERATED || return_date | datetime | YES | | NULL | |+-------------+----------+------+-----+-------------------+-------------------+id - 프라이머리 키book_id - 외부 키 참조 books.idcustomer_id - 외부 키 참조 customer.id
이제 고객님의 전화나 작성자 이름 등 예약 요건이 주어지면 해당 주문과 관련된 모든 예약을 반품하고 싶습니다.설명을 위해 샘플 부킹 API를 보여 드리겠습니다.
예약 담당자:
@RestController
@RequestMapping("/bookings")
public class BookingController {
@Autowired
BookingService bookingService;
// some booking apis which return Booking objects
@GetMapping
public List<Booking> getAllBookingsBy(@RequestParam("phone") String phone,
@RequestParam("authors") List<String> authors) {
return bookingService.getAllBy(phone, authors);
}
@PostMapping
public Booking addBooking(@RequestBody Booking booking) {
bookingService.saveBooking(booking);
return booking;
}
}
예약 서비스 클래스:
@Service
public class BookingService {
@Autowired
private BookingRepository bookingRepository;
// some booking service methods
// get all bookings booked by a customer with matching phone number and books written by a given list of authors
public List<Booking> getAllBy(String phone, List<String> authors) {
return bookingRepository.queryBy(phone, authors);
}
public void saveBooking(Booking booking) {
bookingRepository.save(booking);
}
}
저장소 클래스 예약:
@Repository
public interface BookingRepository extends JpaRepository<Booking, Integer> {
// some booking repository methods
@Query(value = "SELECT * FROM bookings bs WHERE " +
"EXISTS (SELECT 1 FROM customer c WHERE bs.customer_id = c.id AND c.phone = :phone) " +
"AND EXISTS (SELECT 1 FROM books b WHERE b.id = bs.book_id AND b.author IN :authors)",
nativeQuery = true)
List<Booking> queryBy(@Param("phone") String phone,
@Param("authors") List<String> authors);
}
표시된 예약 컨트롤러를 누르면 다음과 같은 예약 개체가 반환됩니다.
[
{
"id": 3,
"book_id": 5,
"customer_id": 2,
"issue_date": "2019-02-04 01:45:21",
"return_date": null
}
]
하지만 저는 그렇게 하고 싶지 않아요, 그 예약의 고객 이름과 책 이름도 함께 돌려드리고 싶습니다.컨트롤러에 의해 반환된 예약 오브젝트는 다음과 같습니다.
[
{
"id": 3,
"book_id": 5,
"customer_id": 2,
"issue_date": "2019-02-04 01:45:21",
"return_date": null,
"customer_name": "Cust 2",
"book_name": "Book_2_2",
}
]
누가 좀 도와주실래요?여기서부터는 진행할 수 없어서 꼼짝할 수가 없어요.
############# 편집: 예약 클래스에 단방향 일대일 어소시에이션을 추가했습니다.
@OneToOne
@JoinColumn(name = "book_id", insertable = false, updatable = false)
private Book book;
@OneToOne
@JoinColumn(name = "customer_id", insertable = false, updatable = false)
private Customer customer;
하지만 컨트롤러를 누르면 예약 객체에 Book 객체와 Customer 객체가 모두 표시됩니다.그럼 어떻게 하면 예약 객체에 책 이름과 고객 이름을 반환할 수 있을까요?반환된 예약 개체는 다음과 같습니다.
[
{
"id": 3,
"book_id": 5,
"book": {
"id": 5,
"book_name": "Book_2_2",
"author": "author_2"
},
"customer_id": 2,
"customer": {
"id": 2,
"name": "Cust 2",
"phone": "98765431",
"registered": "2019-02-04 01:13:16"
},
"issue_date": "2019-02-04 01:45:21",
"return_date": null
}
]
또한 예약 컨트롤러의 save() api가 작동하지 않게 되었습니다.Booking 타입의 오브젝트를 전송하면 bookId와 customerId가 0으로 표시되기 때문입니다.이러한 변경은 제가 추가하기 전에는 발생하지 않았습니다.
네가 한 짓은 잘못이다.귀하는 예약을 반환하고 북 네임과 같은 가입 정보가 포함된 엔티티에 마법처럼 역직렬화되기를 기대합니다.그러나 저장소에 대한 선택 쿼리에서 예약을 선택했습니다.구현 시 상황으로 볼 때 예약에는 본서에 대한 정보가 포함되어 있지 않습니다.
먼저 JSON으로 역직렬화하는 항목과 스프링 데이터에 대한 지속성 계층으로 사용하는 항목을 분리해야 합니다.
@OneToOne
/@OneToMany
부킹에서 부킹으로 이어지는 관계입니다.- 부킹으로 매핑한 엔티티/컬렉션에서 빠르게 가져오도록 쿼리를 변경합니다.
- POJO를 만들고 컨트롤러에 의해 반환되는 방식으로 JSON 주석을 추가합니다.
- 지속성 객체 / Book에 숨겨진 컬렉션이 있는 예약과 새로 만든 POJO 간의 매핑
OneToOne으로 매핑할 경우 기본 초기화가 OVER가 되므로 쿼리가 약간 번거로워집니다.
영속 레이어에 매핑이 올바르게 설정되어 있다고 가정하면 쿼리는 다음과 같습니다.
@Query(value = "SELECT * FROM bookings bs WHERE " +
"bs.customer.phone = :phone) " +
"AND bs.book.author IN :authors)")
여기 Hibernate > http://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#associations 에서 작성한 매핑 매뉴얼이 있습니다.
이 쿼리는 테이블을 결합하는 가장 좋은 방법이 아닙니다.좀 더 직관적인 방법은 그런 것이다.
SELECT * FROM bookings
WHERE customer_id in (SELECT id FROM customer WHERE phone = :phone)
AND book_id in (SELECT id FROM books WHERE author IN :authors)
이하의 순서에 따라서 실장할 수 있습니다.
- 응답에 필요한 모든 필드에 대해 getters를 사용하여 새 인터페이스를 만듭니다.
- @Query 내의 쿼리 문자열에서 선택한 열에 이름을 지정해야 합니다.주의: 이러한 이름은 인터페이스에서 작성한 getter와 동기화해야 합니다.
- 이 인터페이스를 저장소 메서드의 반환 유형으로 사용합니다.
자세한 내용은 스프링 데이터 저장 시 투영을 참조하십시오.https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#프로젝트
언급URL : https://stackoverflow.com/questions/54507552/how-to-join-results-of-multiple-tables-in-spring-jpa-repository
'programing' 카테고리의 다른 글
어떻게 하면 php의 JSON 응답 내의 문자열 안에 큰따옴표를 넣을 수 있을까요? (0) | 2023.03.03 |
---|---|
CLOB와 NCLOB의 차이점은 무엇입니까? (0) | 2023.03.03 |
범위 $id로 DOM 요소 가져오기 (0) | 2023.03.03 |
jq에서 CSV 출력을 사용할 때 어레이를 단일 문자열 값으로 결합하려면 어떻게 해야 합니까? (0) | 2023.02.26 |
create-react-app에서 SSL 증명서를 제공하려면 어떻게 해야 합니까? (0) | 2023.02.26 |