6.0 版本是否支持对嵌入实体进行投影?



我正在开发一个搜索 api,我需要在其中返回仅包含请求中要求的字段/属性的资源作为响应。这些字段也可以是子元素。例如 -book.author.name书是父资源,作者是其下的子资源,可能具有多对一的关系。 我在早期版本的休眠 (5.x.x( 中了解到嵌入式实体不支持投影。 所以想知道这个功能是否在 6.0 中添加

当只有一个作者时,是的,你可以对作者进行投影(但你已经在搜索5中,尽管方式不太方便(:

@Entity
@Indexed
class Book {
@Id
private Long id;
@GenericField
private String title;
@ManyToOne
@IndexedEmbedded
private Author author;
// ...getters and setters...
}
@Entity
class Author {
@Id
private Long id;
@GenericField(projectable = Projectable.YES)
private String firstName;
@GenericField(projectable = Projectable.YES)
private String lastName;
@OneToMany(mappedBy = "author")
private List<Book> books = new ArrayList<>();
// ...getters and setters...
}
class MyProjectedAuthor {
public final String firstName;
public final String lastName;
public MyProjectedAuthor(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
SearchSession searchSession = Search.session(entityManager);
List<MyProjectedAuthor> projectedAuthors = searchSession.search(Book.class)
.asProjection(f -> f.composite(
MyProjectedAuthor::new,
f.field("author.firstName", String.class),
f.field("author.lastName", String.class),
))
.predicate(f -> f.matchAll())
.fetchHits(20);

尚不支持多值投影(例如,如果每本书有多个作者(,但我们将在 6.0.0 版本之前对其进行处理:https://hibernate.atlassian.net/browse/HSEARCH-3391

如果您谈论的是从数据库加载作者而不是投影,那么还没有这样的内置功能。当我们解决HSEARCH-3071时,我们会对此进行调查,但我不知道需要多长时间。

解决方法是,对于单值关联,您可以手动实现加载:

@Entity
@Indexed
class Book {
@Id
private Long id;
@GenericField
private String title;
@ManyToOne
@IndexedEmbedded
private Author author;
// ...getters and setters...
}
@Entity
class Author {
@Id
@GenericField(projectable = Projectable.YES)
private Long id;
private String firstName;
private String lastName;
@OneToMany(mappedBy = "author")
private List<Book> books = new ArrayList<>();
// ...getters and setters...
}
SearchSession searchSession = Search.session(entityManager);
List<Author> authors = searchSession.search(Book.class)
.asProjection(f -> f.composite(
authorId -> entityManager.getReference(Author.class, authorId),
f.field("author.id", Long.class)
))
.predicate(f -> f.matchAll())
.fetchHits(20);
// Or, for more efficient loading:
SearchSession searchSession = Search.session(entityManager);
List<Long> authorIds = searchSession.search(Book.class)
.asProjection(f -> f.field("author.id", Long.class))
.predicate(f -> f.matchAll())
.fetchHits(20);
List<Author> authors = entityManager.unwrap(Session.class).byMultipleIds(Author.class)
.withBatchSize(20)
.multiLoad(authorIds);

编辑:根据您的评论,您的问题与许多领域有关,而不仅仅是作者。本质上,您关心的是尽可能少地加载关联。

Hibernate ORM 中此问题的最常见解决方案是将所有关联的获取模式设置为懒惰(无论如何,这是默认情况下您应该做的(。 然后在搜索时,甚至不要考虑加载:只需让Hibernate搜索检索您需要的实体即可。此时不会加载关联。 然后,当您将实体序列化为 JSON 时,将仅加载您实际使用的关联。如果您正确设置了默认的批量提取大小(使用hibernate.default_batch_fetch_size,则性能应该与使用更复杂的解决方案实现的性能相当,而开发时间只是其中的一小部分。

如果你真的想急切地获取一些关联,最简单的解决方案可能是利用JPA的实体图:它们告诉Hibernate ORM在加载Book实体时准确加载哪些关联。

Hibernate Search 6中还没有内置的功能,但您可以手动执行此操作:

@Entity
@Indexed
class Book {
@Id
private Long id;
@GenericField
private String title;
@ManyToOne // No need for an @IndexedEmbedded with this solution, at least not for loading
private Author author;
// ...getters and setters...
}
@Entity
class Author {
@Id // No need for an indexed ID with this solution, at least not for loading
private Long id;
private String firstName;
private String lastName;
@OneToMany(mappedBy = "author")
private List<Book> books = new ArrayList<>();
// ...getters and setters...
}
SearchSession searchSession = Search.session(entityManager);
List<Long> bookIds = searchSession.search(Book.class)
.asProjection(f -> f.composite(ref -> (Long)ref.getId(), f.entityReference()))
.predicate(f -> f.matchAll())
.fetchHits(20);
// Note: there are many ways to build a graph, some less verbose than this one.
// See https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#fetching-strategies-dynamic-fetching-entity-graph
javax.persistence.EntityGraph<Book> graph = entityManager.createEntityGraph( Book.class );
graph.addAttributeNode( "author" );
// Ask for the author association to be loaded eagerly
graph.addSubgraph( "author" ).addAttributeNode( "name" );
List<Book> booksWithOnlySomeAssociationsFetched = entityManager.unwrap(Session.class).byMultipleIds(Book.class)
.with(graph, org.hibernate.graph.GraphSemantic.FETCH)
.withBatchSize(20)
.multiLoad(bookIds);

请注意,即使使用此解决方案,您可能也应该在映射(@OnyToMany,...(中将获取模式设置为延迟,因为Hibernate ORM不允许通过获取图使渴望的关联延迟。

最新更新