Hibernate:@OneToMany,避免多个 SELECT

Hibernate: @OneToMany, avoid multiple SELECTs

考虑一个人可以拥有很多车的例子。这就是我们使用 @OneToMany 注释的原因:

@Entity
@Table(name = "person")
public class Person  {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column(name = "phone")
    private String phone;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "owner")
    private List<Car> cars = new ArrayList<Car>();

    public Person() {
    }

    // Getters setters
}

@Entity
@Table(name = "cars")
public class Car {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToOne
    private Person owner;

    @Column(name = "description")
    private String description;

    public Car() {
    }

    // Getters setters

坚持不懈,一切正常,但我想改变一些东西来满足我的需要。当程序启动时,我希望所有人都加载到内存中,比方说在 List<Person> 中。所以,我这样做:

List<Person> persons = session.createQuery("from Person", Person.class).list();

但是,Hibernate 也会加载汽车,这是有道理的。 有没有办法阻止它这样做?

这样做的原因是因为执行以下操作会更快:

List<Person> persons = loadAllPersonsFromDatabase();
List<Car> allCars = loadAllCarsFromDatabase();
for (Person p : persons)
{
    List<Car> carsOfThisPerson = findCarsOfThisPerson(allCars,p);
    p.setCars(carsOfThisPerson);
}

因为我想要所有的东西都在记忆中。

我知道将一个数据库的所有记录都加载到内存中并不常见,这是因为一个是数据库,另一个是内存。但是,我的环境是桌面连接,查询速度有点慢(因为数据库是独立的),我确信这些记录永远不会多到会导致 OutOfMemoryError.

我认为将 FetchType.EAGER 更改为 FetchType.LAZY 对我不起作用也很重要。这将是相同的,因为在我加载数据后,我为所有人调用 getCars()

@crizis 在评论中有一个很好的观点。我不简单地删除这两个 (Person,Car) 的关系 (do @Transient List<Car> cars) 的原因是因为我想要持久性提供的其他功能。我的意思是,如果我这样做:

Car car = getCarByPerson("John");
car.setDescription("John");

我可以通过以下方式拯救这辆车:

hibernateSession.save(john);

而不是通过:hibernateSession.save(car);

此外,如果我删除 John,我希望他的所有汽车也被删除(这就是 CASCADE 的用武之地),而不是强迫我:

for (Car c : john.getCars())
    hibernateSession.delete(c);

或其他什么。

创建内存转储并对其进行分析,以查看是什么在使用所有内存。也许您只需要增加最大堆大小。 你也可以让它变得懒惰并在汽车集合上使用 Hibernate.initialize 。这样,我什至认为可以使用像这样的获取模式子选择。 如果没有任何帮助,我可以推荐使用 Blaze-Persistence 实体视图将加载状态减少到最低限度,并确保使用 SUBSELECT 获取策略。

经过一些实验和网络搜索后,我发现使用 FetchMode.SELECT 将完全满足我的需求,这正是我一直在寻找的。我改变了:

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "owner")
private List<Car> cars = new ArrayList<Car>();

至:

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "owner")
@Fetch(value = FetchMode.SUBSELECT)
private List<Car> cars = new ArrayList<Car>();

实际上它只做了 2 次选择。