Я новичок в 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;
}
}
Теперь схемы таблиц для соответствующих сущностей выглядят следующим образом:
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
Теперь то, что я хочу сделать, это дать некоторые критерии бронирования, такие как телефон клиента или имя автора и т. д., Я хочу вернуть все бронирования, связанные с этим заказом. Я покажу пример 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;
}
}
Класс обслуживания бронирования:
@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",
}
]
Может ли кто-нибудь помочь в этом? Я застрял, так как не могу продолжить.
#################### Обновлено: Я добавил эти однонаправленные связи «один к одному» в свой класс Booking:
@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
}
]
Кроме того, теперь мой API-интерфейс save() в моем контроллере бронирования не работает, потому что, когда я отправляю ему объект типа 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)
Он спрашивает, как получить конечное представление json. У него нет ни правильных отображений json, ни правильных отображений гибернации. SQL-запрос — его последняя проблема в коде.
То, что вы делаете, неправильно. Вы возвращаете Booking и ожидаете, что он волшебным образом десериализуется в сущность, содержащую информацию о присоединении, такую как Book Name. Но в вашем запросе выбора в репозитории вы выбрали файл Booking. Как обстоят дела в вашей реализации, Booking не содержит информации о Книге.
Сначала вам нужно разделить то, что вы будете десериализовать как JSON, и то, что вы будете использовать в качестве уровня сохранения по отношению к вашим данным Spring.
@OneToOne/@OneToMany от Booking к Book.На самом деле, если вы сопоставляете как OneToOne, инициализация по умолчанию становится EAGER, поэтому ваш запрос становится немного ненужным.
Если мы предполагаем, что у вас есть сопоставления прямо на постоянном уровне, ваш запрос будет выглядеть так:
@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
Я не ожидаю, что он «волшебным образом десериализуется» в другой объект, я знаю, что он вернет объект Booking. Я просто хочу знать, какие изменения я могу добавить, чтобы получить имя клиента и название книги в дополнение к объекту бронирования.
Читайте выше :) Объяснено, спрашивайте, если что-то не понимаете.
@user3248186 user3248186 ваша главная ошибка в том, что вы не используете Hibernate для того, чем он является — Object RElational Mapping. Нанесите на карту все ваши отношения с соответствующими аннотациями.
Не могли бы вы добавить какие-либо ресурсы, откуда я могу узнать больше об этом?
добавил ссылку на официальную документацию.
Проверил сопоставления OneToOne, ManyToOne и т. д. и аннотации joinColumn, но я не совсем понимаю аннотации JSON для настройки объекта, отправляемого контроллером.
@user3248186 user3248186 не смешивайте аннотации json с объектами гибернации. Напишите второй POJO для представления вашего dtos
Вы можете реализовать его в соответствии с приведенными ниже шагами.
Для получения дополнительной информации вы можете обратиться к Projects in spring data rest. https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
Я думаю, проблема в том, как получить различные наборы результатов, не создавая новую сущность. Должно быть только изменение уровня репозитория?