【问题标题】:Does hibernate search - 6.0 version support projection on embedded entities?hibernate search - 6.0 版本是否支持对嵌入式实体的投影?
【发布时间】:2019-12-09 16:02:11
【问题描述】:

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

【问题讨论】:

    标签: hibernate-search


    【解决方案1】:

    当只有一个作者时,是的,您可以对作者进行投影(但您已经可以在搜索 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_sizehere,则性能应该与您使用更复杂的解决方案所获得的性能相当,并且只需一小部分开发时间。

    如果您真的想急切地获取一些关联,最简单的解决方案可能是利用 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

    【讨论】:

    • 我有大量子实体和许多字段,我必须对其应用字段过滤。所以上述5中的解决方案对我来说不太方便。现在,REST api 开发的最佳实践要求搜索 api 上的字段,因为客户端并不总是需要所有字段,并且有时资源数据太大以至于会影响应用程序性能和带宽,从而将整个资源数据返回。跨度>
    • @Retheesh 在我的回答中添加了更多内容。
    • 感谢您的建议,将不得不尝试选择一个最佳的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-14
    相关资源
    最近更新 更多