I have a performance issue on search queries with multiple joins on the table with 250000+ records. The best time that I achieve is 1.5 seconds with default pagination and sorting provided by JPA. Also, I tried to add indexes on columns, but the time remains the same because of joins. Is there any way to boost the performance of the query?
"select new com.app.e_library.service.dto.BookDto(book.id,book.isbn," +
" book.title, book.publicationYear, book.pageCount, genre.name," +
" book.bookStatus, publisher.publisherName, author.name) " +
"from BookEntity book " +
"inner join book.bookGenre genre " +
"inner join book.publisher publisher " +
"inner join book.author author " +
"where book.isbn like :key% or " +
"book.title like :key% or " +
"trim(book.publicationYear) like :key% or " +
"genre.name like :key% or " +
"publisher.publisherName like :key% or " +
"author.name like :key%"
And query generated by hibernate.
Hibernate:
select
bookentity0_.id as col_0_0_,
bookentity0_.isbn as col_1_0_,
bookentity0_.title as col_2_0_,
bookentity0_.publication_year as col_3_0_,
bookentity0_.page_count as col_4_0_,
bookgenree1_.name as col_5_0_,
bookentity0_.book_status as col_6_0_,
publishere2_.name as col_7_0_,
authorenti3_.name as col_8_0_
from
book bookentity0_
inner join
book_genre bookgenree1_
on bookentity0_.genre_id=bookgenree1_.id
inner join
publisher publishere2_
on bookentity0_.publisher_id=publishere2_.id
inner join
author authorenti3_
on bookentity0_.author_id=authorenti3_.id
where
bookentity0_.isbn like ?
or bookentity0_.title like ?
or trim(bookentity0_.publication_year) like ?
or bookgenree1_.name like ?
or publishere2_.name like ?
or authorenti3_.name like ?
order by
bookentity0_.id asc limit ?
Hibernate:
select
count(bookentity0_.id) as col_0_0_
from
book bookentity0_
inner join
book_genre bookgenree1_
on bookentity0_.genre_id=bookgenree1_.id
inner join
publisher publishere2_
on bookentity0_.publisher_id=publishere2_.id
inner join
author authorenti3_
on bookentity0_.author_id=authorenti3_.id
where
bookentity0_.isbn like ?
or bookentity0_.title like ?
or trim(bookentity0_.publication_year) like ?
or bookgenree1_.name like ?
or publishere2_.name like ?
or authorenti3_.name like ?
Book Entity
@Entity
@Table(
name = "book",
uniqueConstraints = {
@UniqueConstraint(name = "book_isbn_unique",column Names = "isbn")
},
indexes = {
@Index(name = "isbn_index", columnList = "isbn"),
@Index(name = "title_index", columnList = "title"),
}
)
public class BookEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "isbn", nullable = false)
@NotBlank
private String isbn;
@Column(name = "title", nullable = false)
@NotBlank
private String title;
@Column(name = "publication_year")
@Valid
private short publicationYear;
@Column(name = "page_count")
@Valid
@Range(min = 50, max = 5000)
private int pageCount;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="genre_id",referencedColumnName = "id", nullable=false)
@ToString.Exclude
private BookGenreEntity bookGenre;
@Column(name = "book_status", nullable = false)
@Enumerated(EnumType.STRING)
private BookStatusType bookStatus;
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "publisher_id", referencedColumnName = "id", nullable = false)
@NonNull
@ToString.Exclude
private PublisherEntity publisher;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "pick_detail_id", referencedColumnName = "id")
@ToString.Exclude
private PickDetailEntity pickDetail;
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "author_id", referencedColumnName = "id", nullable = false)
@NonNull
@ToString.Exclude
private AuthorEntity author;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "book_image_id", referencedColumnName = "id")
@ToString.Exclude
private BookImageEntity bookImage;
Book Genre Entity
@Entity
@Table(
name = "book_genre",
uniqueConstraints = {
@UniqueConstraint(name = "book_genre_name_unique", columnNames = "name")
},
indexes = {
@Index(name = "name_index", columnList = "name"),
}
)
public class BookGenreEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name", nullable = false)
@NotBlank
private String name;
@OneToMany(
targetEntity = BookEntity.class,
mappedBy = "bookGenre",
cascade=CascadeType.ALL,
fetch = FetchType.LAZY,
orphanRemoval = true)
@ToString.Exclude
private List<BookEntity> books;
Publisher Entity
@Entity
@Table(
name = "publisher",
uniqueConstraints = {
@UniqueConstraint(name = "publisher_name_unique", columnNames = "name")
},
indexes = {
@Index(name = "name_index", columnList = "name")
}
)
public class PublisherEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name", nullable = false)
@NotBlank
private String publisherName;
@OneToMany(
targetEntity = BookEntity.class,
mappedBy = "publisher",
cascade=CascadeType.ALL,
fetch = FetchType.LAZY)
@ToString.Exclude
private List<BookEntity> books;
Author Entity
@Entity
@Table(
name = "author",
uniqueConstraints = {
@UniqueConstraint(name = "author_name_unique", columnNames = "name")
},
indexes = {
@Index(name = "name_index", columnList = "name")
}
)
public class AuthorEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name", nullable = false)
@NotBlank
private String name;
@OneToMany(
targetEntity = BookEntity.class,
mappedBy = "author",
cascade=CascadeType.ALL,
fetch = FetchType.LAZY)
@ToString.Exclude
private List<BookEntity> books;
You don't have to specify the referencedColumnName
in your @JoinColumn
You have a @UniqueConstraint
on BookEntity#isbn
so there's no need to add an extra index.
You need an index on BookEntity#publication_year
:
indexes = {
@Index(name = "title_index", columnList = "title"),
@Index(name = "publication_year_index", columnList = "publication_year"),
}
You can try this query:
@NamedQuery(name="BookEntity.search", query="select book from BookEntity book"
+ " inner join fetch book.genre genre"
+ " inner join fetch book.publisher publisher"
+ " inner join fetch book.author or author"
+ " left join fetch book.pickDetail"
+ " where book.isbn like :key"
+ " or book.title like :key"
+ " or book.publicationYear = :year"
+ " or genre.name like :key"
+ " or publisher.publisherName like :key"
+ " or author.name like :key")
And this function:
private EntityManager em;
public List<BookEntity> search(String key, short year) {
return em.createNamedQuery("BookEntity.search", BookEntity.class)
.setParameter("key", key + "%")
.setParameter("year", year)
.getResultList();
}
Are you aware that you are doing a sensitive case search? That's not very friendly. Also, the user can't search with a word in the middle of the title.
To avoid this you have to use something like:
upper(book.title) like :key
And
setParameter("key", "%" + key.toUpperCase() + "%")
But this will trigger a full scan.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.