Hibernate在调用saveOrUpdate时执行不需要的SELECT

时间:2011-01-05 02:15:21

标签: java hibernate

假设我有一个House实体,它映射到许多Person实体。然后我加载一个有20个占用者的现有房子。

beginTransaction();
House house = houseDao.find(1L);
commitTransaction();

稍后在代码中,我可以向众议院添加一个新人:

...
List<Person> people = house.getPeople();
people.add(new Person("Dilbert"));
....

当我拨打电话时:

session.saveOrUpdate(house);

Hibernate执行21次查询:1次选择House,20次选择House中的每个现有人员。

我确信这对我来说是一个小问题,但是,在这种情况下,我应该怎么做才能在房子里添加一个新人,而不会对数据库产生如此大的打击?

这都是在同一个会话中完成的。

2 个答案:

答案 0 :(得分:3)

因为你真的没有给出很多对象的定义,所以我会做一些假设。

  1. 人的家是一对多
  2. 人只能属于一个房子
  3. 确保房屋的定义如下:

    @Entity
    public class House implements Serializable {
        @Id
        private int id;
    
        @OneToMany(mappedBy="house")
        private Set<Person> people;
    
        ... rest of your class
    }
    
    
    @Entity
    public class Person implements Serializable {
    
        @Id
        private int id;
    
        @ManyToOne(targetEntity=House.class)
        private House house;
    
        @Column(name="person_name")
        private String name
    
        ... rest of your class
    
        public Person(House house, String name) {
           this.house = house;
           this.name = name;
        }
    }
    

    现在你的代码:

    beginTransaction();
    House house = houseDao.find(1L);
    commitTransaction()
    
    
    ... your magic
    
    Person person = new Person(house,"Dilbert");
    
    session.saveOrUpdate(person);
    

    在上面的示例中,当您使用父/子关系时(无关紧要,它是一对多或多对多),您可以通过子关系建立关系。我通常不会通过父母进行全面更新。在你的例子中,你会看到很多头脑。当你开始工作的地方有成千上万的记录时,它就变得不可能了。

    根据您的模型,要研究的另一件事是对列表的注释进行细微更改。一个例子:

    @Entity
    public class House implements Serializable {
        @Id
        private int id;
    
        @OneToMany(mappedBy="house")
        @Fetch(FetchMode.JOIN)
        private Set<Person> people;
    
        ... rest of your class
    }
    

    鉴于@Fetch不是JPA规范的一部分,而是一个hibernate注释,这取决于你的模型可以提供很大的性能提升,因为它会在单个查询中抓住house对象和所有人。如果您限制属于房屋的房屋和人数,这非常有效。如果你得到非常大的结果集,这可能不是一个好的情况。下一个例子可能更合适:

    @Entity
    public class House implements Serializable {
        @Id
        private int id;
    
        @OneToMany(mappedBy="house")
        @Fetch(FetchMode.SUBSELECT)
        private Set<Person> people;
    
        ... rest of your class
    }
    

    这将使用两个查询来获取所有对象。对House对象的一个​​查询(或根据查询乘以house对象)和对于house对象的所有人的一个查询。

答案 1 :(得分:0)

我认为这是你问题的潜在答案,但也许不是你想的那样。

我还没有找到一种让Hibernate按照你想要的方式运行的方法(它没有那种级别的粒度)。如果你愿意,我相信你可以自己编码。但如果您真的担心数据库的性能损失,请解决问题并专注于如何最大限度地减少数据库上的负载。

优化从Hibernate到DB的调用的最佳方法是确保所有的hibernate对象表都被编入索引。 Hibernate使用父对象的主键调用表。这20个查询如果达到索引并且位置很快,则占用的处理时间非常短。 DB是为处理大量这样的事务而构建的 - 昂贵的部分是将数据定位在磁盘上并加载它以供您使用。

最后,我还要添加一个对象缓存。这可能会消除数据库的往返,从而为程序节省大量的I / O等待时间。