Java 如何在Spring JPA存储库中连接多个表的结果

Java 如何在Spring JPA存储库中连接多个表的结果,java,spring,spring-boot,spring-data-jpa,Java,Spring,Spring Boot,Spring Data Jpa,我是Spring新手,不知道如何连接多个表以返回一些结果。我尝试实现一个小型库应用程序,如下所示 我的实体类-Book、Customer、Bookings java-图书馆中提供的书籍 @Entity @Table(name = "books") public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id&quo

我是Spring新手,不知道如何连接多个表以返回一些结果。我尝试实现一个小型库应用程序,如下所示

我的实体类-Book、Customer、Bookings

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;
    }
}
@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;
    }
}
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;
    }
}
@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;
    }
}
现在,各个实体的表模式如下所示:

books: +-----------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | book_name | varchar(255) | NO | | NULL | | | author | varchar(255) | YES | | NULL | | +-----------+--------------+------+-----+---------+----------------+ id - primary key customer: +------------+--------------+------+-----+-------------------+-------------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+-------------------+-------------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(255) | NO | | NULL | | | registered | datetime | YES | | CURRENT_TIMESTAMP | DEFAULT_GENERATED | | phone | varchar(15) | YES | UNI | NULL | | +------------+--------------+------+-----+-------------------+-------------------+ id - primary key bookings: +-------------+----------+------+-----+-------------------+-------------------+ | Field | Type | Null | Key | Default | Extra | +-------------+----------+------+-----+-------------------+-------------------+ | id | int(11) | NO | 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 - primary key book_id - foreign key references books.id customer_id - foreign key references customer.id 但我不想这样,我想和他们一起返回预订客户的姓名以及书的名称。因此,我希望控制器返回的预订对象如下所示:

[
    {
        "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",
    }
]
@Query(value = "SELECT * FROM bookings bs WHERE " +
            "bs.customer.phone = :phone) " +
            "AND  bs.book.author IN :authors)")
有人能帮忙吗?我被困住了,因为我无法从这里继续前进

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

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

@OneToOne
@JoinColumn(name = "customer_id", insertable = false, updatable = false)
private Customer customer;
但现在,当我点击控制器时,我的预订对象中包含了整本书和客户对象。那么,我该怎么做才能在booking对象中返回bookname和customer name呢?下面是我现在返回的预订对象的外观:

[
    {
        "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 controller中的save api不起作用,因为当我向它发送booking类型的对象时,bookId和customerId以某种方式显示为0,这在我添加这些更改之前是不会发生的。

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

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)

你所做的是错误的。您返回的是Booking,您希望它能够反序列化为一个实体,该实体包含诸如Book Name之类的连接信息。但在对存储库的select查询中,您选择了预订。按照您的实施方式,预订并不包含有关该书的信息

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

首先,从预订到预订建立@OneToOne/@OneToMany关系。 将查询更改为对已映射为Book的实体/集合执行即时抓取。 制作一个POJO,并按照您希望控制器返回的方式使用JSON注释对其进行注释。 在书本上隐藏收藏的持久化对象/预订和新创建的POJO之间映射 实际上,如果您映射为OneToOne,那么默认的初始化将变得非常紧迫,因此您的查询变得有点不必要

如果我们假定您的映射正好位于持久层,那么您的查询将如下所示:

[
    {
        "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",
    }
]
@Query(value = "SELECT * FROM bookings bs WHERE " +
            "bs.customer.phone = :phone) " +
            "AND  bs.book.author IN :authors)")
这是来自Hibernate的映射文档>

您可以按照以下步骤实施

使用getter为响应中需要的所有字段创建一个新接口。 在@query内的查询字符串中,您需要为select中的列提供名称。注意:这些名称需要与您在接口中创建的getter同步。 将此接口用作存储库方法的返回类型。 有关更多信息,请参考spring data rest中的投影。

他在问如何获得最终的json表示。他既没有正确的json映射,也没有正确的hibernate映射。sql查询是他在代码中遇到的最后一个问题。我不希望它将magicaly反序列化为另一个实体,我知道它将返回Booking对象。我只是想知道除了booking对象之外,我还可以添加哪些更改来获取客户名称和图书名称。请阅读上面的内容:这是解释,询问您是否不了解某些内容。@user3248186您的主要错误是您没有使用Hibernate进行对象关系映射。使用适当的注释映射您的所有关系。您可以添加我可以了解更多信息的资源吗?添加了官方文档的链接。我认为问题在于如何在不创建新实体的情况下获得各种结果集。应该只是存储库级别的更改吗?