#jpa #spring-data-jpa #jpql
#jpa #spring-data-jpa #jpql
Вопрос:
Интересно, есть ли способ оптимизировать процесс генерации JPA-запроса (JPQL) вместо использования собственного.
для простоты моя модель выглядит следующим образом:
@Entity
@Table(name = "b_books")
public class Book {
@Id
@Column(name = "book_id", updatable = false, nullable = false, unique = true)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="book_generator")
@SequenceGenerator(name="book_generator", sequenceName = "books_seq", allocationSize=1)
private Integer id;
@Column(name = "book_titl_tx")
private String title;
private int yearOfPublication;
@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@JoinTable(name = "b_book_authors",
joinColumns = @JoinColumn(name = "book_id"),
inverseJoinColumns = @JoinColumn(name = "auth_id"))
private List<Author> authors = new ArrayList<Author>();
//getter, setters, etc
}
@Entity
@Table(name = "b_authors")
public class Author {
@Id
@Column(name = "auth_id", nullable = false, unique = true)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="authors_generator")
@SequenceGenerator(name="authors_generator", sequenceName = "authors_seq", allocationSize=1)
private Integer id;
@Column(name = "auth_last_name_tx")
private String lastName;
@ManyToMany(mappedBy = "authors")
private List<Book> books = new ArrayList<Book>();
//getters, setters, etc
}
На стороне базы данных у меня есть три таблицы:
- B_BOOKS
- B_AUTHORS
- B_BOOK_AUTHORS, который содержит отношение ManyToMany
Я хотел бы запросить в БД количество книг для каждого автора
public interface AuthorRepository extends JpaRepository<Author, Integer>{
@Query(value = "SELECT a.lastName AS lastName, "
" COUNT(a) AS booksCount"
" FROM Author AS a "
" LEFT JOIN a.books AS b"
" GROUP BY a.lastName")
List<IAuthorInfo> getAuthorInfo();
}
Я использую проекцию на основе интерфейса
public interface IAuthorInfo {
String getLastName();
Integer getBooksCount();
}
А теперь к главному вопросу: в этой модели сгенерированный JPA запрос имеет одно избыточное соединение, как от него избавиться?
АКТУАЛЬНО:
SELECT
author0_.auth_last_name_tx AS col_2_0_,
COUNT(author0_.auth_id) AS col_3_0_
FROM
admin.b_authors author0_
LEFT OUTER JOIN admin.b_book_authors books1_ ON author0_.auth_id = books1_.auth_id
LEFT OUTER JOIN admin.b_books book2_ ON books1_.book_id = book2_.book_id
GROUP BY
author0_.auth_last_name_tx
ОЖИДАЕМЫЙ
SELECT
author0_.auth_last_name_tx AS col_2_0_,
COUNT(author0_.auth_id) AS col_3_0_
FROM
admin.b_authors author0_
LEFT OUTER JOIN admin.b_book_authors books1_ ON author0_.auth_id = books1_.auth_id
GROUP BY
author0_.auth_last_name_tx
Ответ №1:
Вы можете использовать size()
функцию:
SELECT a.lastName AS lastName, size(a.books) AS booksCount FROM Author AS a GROUP BY a.lastName
Я не тестировал, но я бы предположил, что удаление LEFT JOIN a.books AS b
из вашего запроса решит эту проблему.
Ответ №2:
Спасибо, это LEFT JOIN a.books AS b
действительно удалено.
Теперь он генерирует SQL:
SELECT author0_.auth_last_name_tx AS col_2_0_,
(SELECT COUNT(books1_.auth_id)
FROM b_book_authors books1_
WHERE author0_.auth_id = books1_.auth_id) AS col_3_0_
FROM b_authors author0_
GROUP BY author0_.auth_last_name_tx;
что удивительно работает с базой данных H2 (mem) для модульных тестов
, но для Oracle требуется добавление Author.Идентификатор в JPQL
SELECT author0_.auth_id,
author0_.auth_last_name_tx AS col_2_0_,
(SELECT COUNT(books1_.auth_id)
FROM b_book_authors books1_
WHERE author0_.auth_id = books1_.auth_id) AS col_3_0_
FROM b_authors author0_
GROUP BY author0_.auth_id,
author0_.auth_last_name_tx;