hibernate - 使用 JPA 标准省略不需要的 JOIN
问题描述
我的 Sping Boot 应用程序中有三个相关的实体 Author、Category 和 Book:
@Entity
class Category {
@Id
Long id;
// other members, getters and setters
}
@Entity
class Author {
@Id
Long id;
// other members, getters and setters
}
@Entity
class Book {
@Id
Long id;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "author_id", nullable = false)
private Author author;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "category_id", nullable = false)
private Category category;
// getters and setters
}
用户可以按作者和/或类别搜索书籍。为此,我的应用程序提供了一个接收以下 SearchDto 的搜索 REST 端点:
class SearchDTO {
public List<Long> authorIds;
public List<Long> categoryIds;
}
我正在使用标准 API 来编写数据库查询。
class BookRepositoryCustomImpl implements BookRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
public search(SearchDto searchDto) {
var cb = entityManager.getCriteriaBuilder();
var query = cb.createQuery(Book.class);
var result = query.from(Book.class);
var predicates = new ArrayList<Predicate>();
if (searchDto.authorIds != null) {
// Version 1
// predicates.add(book.get(Book_.author).in(searchDto.authorIds));
// Version 2
// var authorJoin = book.join(Book_.author);
// predicates.add(authorJoin.get(Author_.id).in(searchDto.authorIds));
}
// Similar code for category constraint.
query.select(result);
query.where(predicates.toArray(Predicate[]::new));
return entityManager.createQuery(query).getResultList();
}
}
我的两个版本都有效,但各有缺点。版本 1 省略了对 Author 的不必要连接,并直接使用书籍的列 author_id。但是,由于 id 是数值,SQL 语句包含文字,但我希望有绑定变量。设置属性spring.jpa.properties.hibernate.criteria.literal_handling_mode=BIND
时,我得到一个运行时异常,因为提供了一个 Long 变量但需要一个 Author 对象。在 SQL 语句中使用文字时不会发生此错误。
INNER JOIN author ON book.author_id = author.id WHERE author.id IN ($1)
在版本 2 中,我可以使用绑定变量,但 SQL 语句包含对 Author 表 ( )的不必要连接。
我还可以想到版本 3,我将使用它authorRepository.getOne(authorId)
来获取作者对象的代理并使用List<Author>
. 这种方法的缺点是我必须编写所有胶水代码来将 id 转换为实体对象。
是否有适当的方法来省略 JOIN 但能够使用绑定变量?
解决方案
您可以将 id 转换为对Author
with的引用getReference()
:
Author reference = entityManager.getReference(Author.class, authorId);
我不确定 的类型是什么searchDTO.authorIds
,但假设是一个集合:
List<Author> authors = searchDTO.autorhIds.stream()
.map( id -> entityManager.getReference(Author.class, id) )
.collect( Collectors.toList() );
现在您有了一个作者列表,您可以将其用作选项 1 的参数。这将在数据库中创建对作者的惰性引用,而无需实际查询数据库。
更新:我刚刚意识到geOne()
并且getReference()
是同一件事,所以你也可以写:
List<Author> authors = searchDTO.autorhIds.stream()
.map( authorRepository::getOne )
.collect( Collectors.toList() );
推荐阅读
- mysql - Typeorm“ER_BAD_FIELD_ERROR:'where 子句'中的未知列'entity.entityId'”
- microsoft-graph-api - MS Graph API - 有没有办法获取员工所属的预订业务列表?
- qt - Qt NumberAnimation 属性绑定循环问题
- python - 输入张量
以形状()进入循环,但具有形状 一次迭代后 - java - 使用执行器服务在 java swing 上进行光线跟踪并行计算
- javascript - Grafana 在 html 网站中嵌入范围选择器
- label - Gnuplot 不完整数字
- python - 如何在 Python 中制作这个矩阵
- c# - Xamarin 表单中的 MultiBinding:元素为空
- python - 不能在二维列表上使用 pop([i][j]),返回 IndexError: list index out of range