如何在 Spring JPA 存储库中加入多个表的结果

use*_*186 21 java spring spring-data-jpa spring-boot

我是 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在各个实体的表模式如下:

图书:
+-----------+--------------+------+-----+--------- +----------------+
| 领域 | 类型 | 空 | 钥匙 | 默认 | 额外 |
+-----------+--------------+------+-----+--------- +----------------+
| 身份证 | 整数(11) | 否 | PRI | 空 | 自动增量|
| 书名 | varchar(255) | 否 | | 空 | |
| 作者 | varchar(255) | 是 | | 空 | |
+-----------+--------------+------+-----+--------- +----------------+
id - 主键

顾客:
+------------+--------------+------+-----+-------- -----------+-------------------+
| 领域 | 类型 | 空 | 钥匙 | 默认 | 额外 |
+------------+--------------+------+-----+-------- -----------+-------------------+
| 身份证 | 整数(11) | 否 | PRI | 空 | 自动增量|
| 姓名 | varchar(255) | 否 | | 空 | |
| 注册 | 日期时间 | 是 | | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
| 电话 | varchar(15) | 是 | 联合 | 空 | |
+------------+--------------+------+-----+-------- -----------+-------------------+
id - 主键

预订:
+-------------+------------+------+-----+------------ ---+-------------------+
| 领域 | 类型 | 空 | 钥匙 | 默认 | 额外 |
+-------------+------------+------+-----+------------ ---+-------------------+
| 身份证 | 整数(11) | 否 | PRI | 空 | 自动增量|
| book_id | 整数(11) | 否 | 多| 空 | |
| customer_id | 整数(11) | 否 | 多| 空 | |
| 问题日期 | 日期时间 | 是 | | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
| 返回日期 | 日期时间 | 是 | | 空 | |
+-------------+------------+------+-----+------------ ---+-------------------+
id - 主键
book_id - 外键引用books.id
customer_id - 外键引用 customer.id

现在我想要做的是提供一些预订标准,如客户电话或作者姓名等,我想返回与该订单相关的所有预订。我将展示一个示例 Booking 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

预订服务类:

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

预订存储库类:

@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);
}
Run Code Online (Sandbox Code Playgroud)

现在点击显示的预订控制器将返回一个预订对象,如下所示:

[
    {
        "id": 3,
        "book_id": 5,
        "customer_id": 2,
        "issue_date": "2019-02-04 01:45:21",
        "return_date": null
    }
]
Run Code Online (Sandbox Code Playgroud)

但我不想那样,我想与他们一起返回该预订的客户姓名以及书名。所以我希望控制器返回的预订对象看起来像这样:

[
    {
        "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",
    }
]
Run Code Online (Sandbox Code Playgroud)

有人可以帮忙做这件事吗?我被卡住了,因为我无法从这里继续。

################### 编辑:我在预订类中添加了这些单向一对一关联:

@OneToOne
@JoinColumn(name = "book_id", insertable = false, updatable = false)
private Book book;

@OneToOne
@JoinColumn(name = "customer_id", insertable = false, updatable = false)
private Customer customer;
Run Code Online (Sandbox Code Playgroud)

但是现在当我点击我的控制器时,我在我的 Booking 对象中得到了整个 Book 和 Customer 对象。那么我该怎么做才能只返回预订对象中的书名和客户名呢?这是我的 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
    }
]
Run Code Online (Sandbox Code Playgroud)

现在我的预订控制器中的 save() api 不起作用,因为当我向它发送类型为 Booking 的对象时,bookId 和 customerId 不知何故变成了 0,这在我添加这些更改之前没有发生.

Ale*_*rov 6

你的做法是错误的。您正在返回 Booking,并且希望它神奇地反序列化为包含图书名称等连接信息的实体。但是在存储库上的选择查询中,您选择了预订。按照您的实施方式,预订不包含有关书籍的信息。

首先,您需要将反序列化为 JSON 的内容与将用作 Spring 数据持久层的内容分开。

  1. 首先建立从 Booking 到 Book 的@OneToOne/关系。@OneToMany
  2. 更改您的查询以对您映射为 Book 的实体/集合进行急切获取。
  3. 创建一个 POJO 并按照您希望控制器返回的方式使用 JSON 注释对其进行注释。
  4. 您的持久性对象/Book 上带有隐藏集合的 Booking 和您新创建的 POJO 之间的映射

实际上,如果您映射为 OneToOne,则默认初始化会变为 EAGER,因此您的查询变得有点不必要。

如果我们假设您的映射位于持久层中,您的查询将如下所示:

@Query(value = "SELECT * FROM bookings bs WHERE " +
            "bs.customer.phone = :phone) " +
            "AND  bs.book.author IN :authors)")
Run Code Online (Sandbox Code Playgroud)

这是 Hibernate 的映射文档> http://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#associations


Sha*_*her 2

您所拥有的查询不是连接表的最佳方式。更直观的方式是这样的

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)
Run Code Online (Sandbox Code Playgroud)

  • 他问如何获得最终的 json 表示。他既没有正确的 json 映射,也没有正确的 hibernate 映射。sql查询是他在代码中的最后一个问题。 (3认同)