用方法名的spring-data查询重写JPQL-query



我有两个具有多对多关系的实体:

@Entity
@Table(name = "author")
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "author_name", unique = true)
private String authorName;
@ManyToMany // default FetchType.LAZY
@JoinTable(name = "author_book",
joinColumns = @JoinColumn(name = "author_id"),
inverseJoinColumns = @JoinColumn(name = "book_id"))
private Set<Book> books;
// other fields, getters, setters
}

@Entity
@Table(name = "book")
public class Book {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "title", unique = true)
private String title;

// other fields, getters, setters
}

假设,我们在数据库中有一个author, author_name 'Stephen King',有相关的3本书,书名为'book-title-1', 'book-title-2', 'book-title-3'。

我需要得到作者的名字和他的书的特定标题,例如:

查找书名为'book-title-1'或'book-title-2'的作者'Stephen King'

使用如下查询,它可以工作,即我正在获得具有一组两个的作者书籍元素。

public interface AuthorRepository extends JpaRepository<Author, Long> {
@Query("select a from Author a join fetch a.books b " +
"where a.authorName=:authorName and b.title in :titles")
Optional<Author> findByNameAndBookTitles(String authorName, Set<String> titles);
}

Hibernate查询上述方法(简化):

select author.id, book.id, author.author_name, book.title, author_book.author_id, author_book.book_id
from public.author
inner join public.author_book on author.id= author_book.author_id 
inner join public.book on author_book.book_id=book.id 
where author.author_name=? and (book.title in (? , ?))

问题:是否有一种方法来重写它从方法名称的查询?

我试过这样做:

Optional<Author> findByAuthorNameAndBooks_titleIn(String authorName, Set<String> titles);

但是它返回author和all相关的书,而不是两本,所以hibernate会进行以下两个查询(第二次hibernate查询是在第一次访问Set<Book> books时进行的):

Hibernate:
select author.id , author.author_name
from public.author
left outer join public.author_book on author.id = author_book.author_id 
left outer join public.book on author_book.book_id= book.id 
where author_name=? and (book.title in (? , ?))
Hibernate: 
select author_book.author_id, author_book.book_id, book.id, book.title 
from public.author_book
inner join public.book on author_book.book_id=book.id 
where author_book.author_id=?

我建议它可能会与@ManyToMany(fetch = FetchType.EAGER)一起工作,但是hibernate再次进行了两次类似于上面的查询,唯一的区别是第二次查询是在第一次调用图书集之前进行的。

如果您需要任何澄清,请提前告诉我并感谢。

我不建议您使用方法名称约定,因为这只是快速原型化AFAIU的工具。一旦需求发生变化,您可能必须切换回查询,然后您将有乐趣破译该方法名称约定的真正含义。

但是即使你的JPQL/HQL查询也不是真正安全的,因为你返回的是带有过滤集合的托管实体。对集合的简单更改可能导致删除所有过滤掉的书籍。我建议您使用dto来避免所有的问题。

我认为这是火焰持久实体视图的完美用例。

我创建的库允许JPA模型和自定义接口或抽象类定义模型之间的轻松映射,类似于类固醇上的Spring Data projection。其思想是,您以您喜欢的方式定义您的目标结构(域模型),并通过JPQL表达式将属性(getter)映射到实体模型。

您的用例的DTO模型可以像下面这样使用Blaze-Persistence Entity-Views:

@EntityView(Author.class)
public interface AuthorDto {
@IdMapping
Long getId();
String getAuthorName();
Set<BookDto> getBooks();
@EntityView(Book.class)
interface BookDto {
@IdMapping
Long getId();
String getTitle();
}
}

查询是将实体视图应用于查询的问题,最简单的就是通过id进行查询。

AuthorDto a = entityViewManager.find(entityManager, AuthorDto.class, id);

Spring Data集成允许您几乎像Spring Data projection一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<AuthorDto> findAll(Pageable pageable);

最好的部分是,它只会获取实际需要的状态!

在你的特殊情况下,你可以这样使用:

Optional<AuthorDto> findByAuthorNameAndBooks_titleIn(String authorName, Set<String> titles) {
return findOne((root, query, cb) -> {
return cb.and(
cb.equal(root.get("name"), authorName),
root.get("books").get("title").in(titles)
);
});
}

最新更新