代码之家  ›  专栏  ›  技术社区  ›  Jens Jansson

有效提取多个行李

  •  3
  • Jens Jansson  · 技术社区  · 14 年前

    我正在开发一个多语言应用程序。因此,许多对象的名称和描述字段中都有我称之为本地化字符串而不是普通字符串的集合。每个本地化字符串基本上都是一对本地化区域和一个本地化到该区域的字符串。

    让我们以一个实体为例,假设一个书对象。

    public class Book{
    
     @OneToMany
     private List<LocalizedString> names;
    
     @OneToMany
     private List<LocalizedString> description;
    
     //and so on...
    }
    

    当用户请求书籍列表时,它会进行查询以获取所有书籍,在用户选择运行应用程序的区域设置中获取每本书的名称和描述,并将其显示回用户。

    这是可行的,但它是一个主要的性能问题。目前Hibernate进行一次查询以获取所有书籍,之后它将遍历每个对象,并向Hibernate请求特定对象的本地化字符串,从而导致“n+1选择问题”。获取50个实体的列表会在我的服务器日志中生成大约6000行SQL命令。

    我试着让收藏品更受欢迎,但这让我想到了“不能同时取走多个袋子”的问题。

    然后,我尝试将集合的fetch策略设置为subselect,希望它可以对所有书籍执行一个查询,然后执行一个查询,为所有书籍获取所有localizedstring。在这种情况下,选择集并没有像我希望的那样工作,它基本上和我的第一个案例完全一样。

    关于如何优化这个问题,我已经没有什么想法了。

    因此,简而言之,当您提取一个集合时,有哪些提取策略可供选择,并且该集合中的每个元素本身都有一个或多个集合,这些集合必须同时提取。

    2 回复  |  直到 14 年前
        1
  •  3
  •   Arthur Ronald    14 年前

    你说的

    我试着将收藏的“获取”策略设置为“再选择”,希望它能做到。 一个查询 对于所有书籍

    您可以,但您需要访问一些属性来抛出子select

    @Entity
    public class Book{
    
        private List<LocalizedString> nameList = new ArrayList<LocalizedString>();
    
        @OneToMany(cascade=javax.persistence.CascadeType.ALL)
        @org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SUBSELECT)
        public List<LocalizedString> getNameList() {
            return this.nameList;
        }
    
        private List<LocalizedString> descriptionList = new ArrayList<LocalizedString>();
    
        @OneToMany(cascade=javax.persistence.CascadeType.ALL)
        @org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SUBSELECT)
        private List<LocalizedString> getDescriptionList() {
            return this.descriptionList;
        }
    
    
    
    }
    

    如下操作

    public class BookRepository implements Repository {
    
        public List<Book> getAll(BookFetchingStrategy fetchingStrategy) {
            switch(fetchingStrategy) {
                case BOOK_WITH_NAMES_AND_DESCRIPTIONS:
                    List<Book> bookList = session.createQuery("from Book").list();
    
                    // Notice empty statement in order to start each subselect
                    for (Book book : bookList) {
                        for (Name address: book.getNameList());
                        for (Description description: book.getDescriptionList());
                    }
    
                return bookList;
            }
        }
    
        public static enum BookFetchingStrategy {
            BOOK_WITH_NAMES_AND_DESCRIPTIONS;
        }
    
    }
    

    我已经做了下面的一个来填充数据库

    SessionFactory sessionFactory = configuration.buildSessionFactory();
    
    Session session = sessionFactory.openSession();
    session.beginTransaction();
    
    // Ten books
    for (int i = 0; i < 10; i++) {
        Book book = new Book();
        book.setName(RandomStringUtils.random(13, true, false));
    
        // For each book, Ten names and descriptions
        for (int j = 0; j < 10; j++) {
            Name name = new Name();
            name.setSomething(RandomStringUtils.random(13, true, false));
    
            Description description = new Description();
            description.setSomething(RandomStringUtils.random(13, true, false));
    
            book.getNameList().add(name);
            book.getDescriptionList().add(description);
        }
    
        session.save(book);
    }
    
    session.getTransaction().commit();
    session.close();
    

    以及找回

    session = sessionFactory.openSession();
    session.beginTransaction();
    
    List<Book> bookList = session.createQuery("from Book").list();
    
    for (Book book : bookList) {
        for (Name address: book.getNameList());
        for (Description description: book.getDescriptionList());
    }
    
    session.getTransaction().commit();
    session.close();
    

    我懂了

    冬眠:

    select
        book0_.id as id0_,
        book0_.name as name0_ 
    from
        BOOK book0_
    

    休眠:返回100行(如预期)

    select
        namelist0_.BOOK_ID as BOOK3_1_,
        namelist0_.id as id1_,
        namelist0_.id as id1_0_,
        namelist0_.something as something1_0_ 
    from
        NAME namelist0_ 
    where
        namelist0_.BOOK_ID in (
            select
                book0_.id 
            from
                BOOK book0_
        )
    

    休眠:返回100行(如预期)

    select
        descriptio0_.BOOK_ID as BOOK3_1_,
        descriptio0_.id as id1_,
        descriptio0_.id as id2_0_,
        descriptio0_.something as something2_0_ 
    from
        DESCRIPTION descriptio0_ 
    where
        descriptio0_.BOOK_ID in (
            select
                book0_.id 
            from
                BOOK book0_
        )
    

    三个select语句。无“N+1”选择问题。注意我正在使用 物业准入策略 而不是字段。记住这一点。

        2
  •  1
  •   Guillaume    14 年前

    你可以设置一个 batch-size 在您的包上,当一个单元化集合初始化时,Hibernate将用一个查询初始化一些其他集合。

    更多的在 Hibernate doc