web-dev-qa-db-ja.com

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)|いいえ| PRI | NULL | auto_increment | 
 | book_name | varchar(255)| NO | | NULL | | 
 | author | varchar(255)| YES | | NULL | | 
 + ----------- + -------------- + ------ + ----- +- -------- + ---------------- + 
 id-主キー
 
 customer:
 + ------------ + -------------- + ------ + ----- + ------ ------------- + ------------------- + 
 |フィールド|タイプ| Null |キー|デフォルト|追加| 
 + ------------ + -------------- + ------ + ----- +- ------------------ + ------------------- + 
 | id | int(11 )| NO | PRI | NULL | auto_increment | 
 | name | varchar(255)| NO | | NULL | | 
 |登録済み|日時| YES | | CURRENT_TIMESTAMP | DEFAULT_GENERATED | 
 | phone | varchar(15)| YES | UNI | NULL | | 
 + ------------ + ------ -------- + ------ + ----- + ------------------- + ------------------- + 
 id-主キー
 
 bookings:
 + ------------- + ---------- +- ----- + ----- + ------------------- + ------------------ -+ 
 |フィールド|タイプ|ヌル|キー|デフォルト|追加| 
 + ------------- + ---------- + ------ + ----- + ---- --------------- + ------------------- + 
 | id | int(11)|いいえ| PRI | NULL | auto_increment | 
 | book_id | int(11)| NO | MUL | NULL | | 
 | customer_id | int(11)| NO | MUL | NULL | | 
 | issue_date | datetime | YES | | CURRENT_TIMESTAMP | DEFAULT_GENERATED | 
 | return_date | datetime | YES | | NULL | | 
 + ------ ------- + ---------- + ------ + ----- + ------------------ -+ ------------------- + 
 id-主キー
 book_id-外部キーはbooks.id 
 customer_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",
    }
]

誰かがこれを行うのを手伝ってくれませんか?ここから先に進めないので行き詰まっています。

編集:私は私の予約クラスにこれらの一方向の1対1の関連付けを追加しました:

@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オブジェクト全体がBookingオブジェクトに含まれます。では、予約オブジェクトで本の名前と顧客の名前を返すために何ができるでしょうか?返された私のBookingオブジェクトは次のようになります。

[
    {
        "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
    }
]

また、Bookingタイプのオブジェクトを送信すると、bookIdとcustomerIdが何らかの理由で0になってしまうため、予約コントローラーのsave()APIが機能しなくなりました。 。

7
user3248186

あなたが持っているクエリはテーブルを結合するための最良の方法ではありません。より直感的な方法はそのようなものです

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)
0

以下の手順に従って実装できます。

  1. 応答で必要なすべてのフィールドのゲッターを使用して、新しいインターフェースを作成します。
  2. @Query内のクエリ文字列で、selectの列に名前を指定する必要があります。注:これらの名前は、インターフェースで作成するゲッターと同期している必要があります。
  3. このインターフェイスは、リポジトリメソッドの戻り値の型として使用します。

詳細については、Spring Data RestのProjectionsを参照してください。 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

0
Dheeraj Kadam