我正在开发一个搜索API,在该API中,我仅需要返回请求中的字段/属性,以返回资源。字段也可以是子元素。例如- book.author.name ,其中 book 是父资源, author 是其下的子资源,可能具有多对一关系。 我在较早版本的休眠(5.x.x)投影中了解到嵌入式实体不支持投影。 因此想知道是否在6.0中添加了此功能
答案 0 :(得分:0)
只有一位作者时,可以,您可以对作者进行投影(但是您可能已经在Search 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 Search检索所需的实体即可。届时将不会加载关联。
然后,当您将实体序列化为JSON时,只会加载您实际使用的关联。如果您正确设置了默认的批量提取大小(使用hibernate.default_batch_fetch_size
,here,则在开发时间的一小部分,性能应该可以与使用更复杂的解决方案所达到的性能相媲美。
如果您真的想急切地获取某些关联,最简单的解决方案可能是利用JPA的实体图:它们告诉Hibernate ORM在加载Book实体时准确加载哪些关联。
Hibernate Search 6 yet中没有内置功能,但是您可以手动进行:
@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 doesn't allow making an eager association lazy though a fetch graph。