无需数据库调用即可获取惰性 PersistentCollection
当您已经知道父实体的 id 并且还知道该实体实际上存在于数据库中时,有没有办法获取实体的 Collection 属性,无需触发数据库查询?
获取此类集合的最简单方法当然是使用 EntityManager.find(),但它会产生不必要的数据库查询:
MyEntity entity = em.find(MyEntity.class, id); // prints "select ... from MyEntity"
Collection c = entity.getSomeChildren();
我曾假设 EntityManager.getReference() code> 可以解决这个问题,但不幸的是,即使使用 getReference()
,当调用 entity.getSomeChildren()
时,数据库也会被查询,尽管它是一个FetchType.LAZY
属性:
MyEntity entity = em.getReference(MyEntity.class, id);
Collection c = entity.getSomeChildren(); // prints "select ... from MyEntity"
幸运的是,它不会发出 "select ... from MyEntity_SomeChildren"
查询,除非我实际上在 c
上调用某些方法,我知道最后一个效果正是由于 FetchType.LAZY 注释而产生的。但我认为即使是第一个查询也是多余的。
这是 MyEntity
的代码
@Entity
public class MyEntity {
private String id;
private Collection<SomeChildren> someChildren;
@Id
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@OneToMany(fetch = FetchType.LAZY)
public Collection<SomeChildren> getSomeChildren() {
return someChildren;
}
public void setSomeChildren(Collection<SomeChildren> someCollection) {
this.someChildren = someCollection;
}
}
更新:为什么我需要这个? 完整的场景是这样的:我有一个 MyEntity
实例,它是从使用 new
运算符的查询中获得的:
@SuppressWarnings({"unchecked"})
List<MyEntity> list = em.createQuery("select new MyEntity(...) from MyEntity e where ...").getResultList();
MyEntity original = list.get(0);
return original;
这工作正常,但有时(并且只有有时)我需要entity.getSomeChildren()
。因此我不想将它传递给构造函数,所以我想到了这个技巧:
MyEntity original = list.get(0);
MyEntity entity = em.getReference(MyEntity.class, original.getId());
Collection c = entity.getSomeChildren(); // prints "select ... from MyEntity where id=?"
original.setSomeChildren(c);
return original;
...希望它不会发出数据库查询,但它显然会发出:(
Is there a way to get a Collection property of an entity when you already know the id of the parent entity and you also know that the entity actually exists on the database, without triggering a database query?
The simplest way to get such a collection is of course to use a EntityManager.find()
, but it makes an an unnecessary database query:
MyEntity entity = em.find(MyEntity.class, id); // prints "select ... from MyEntity"
Collection c = entity.getSomeChildren();
I had assumed that EntityManager.getReference()
will do the trick but unfortunately even with getReference()
the database is queried when entity.getSomeChildren()
is called, despite it being a FetchType.LAZY
property:
MyEntity entity = em.getReference(MyEntity.class, id);
Collection c = entity.getSomeChildren(); // prints "select ... from MyEntity"
Fortunately it does not issue a "select ... from MyEntity_SomeChildren"
query unless I actually call some method on c
, and I understand that this last effect is exactly because of the FetchType.LAZY
annotation. But I think even the first query is redundant.
Here is the code for MyEntity
@Entity
public class MyEntity {
private String id;
private Collection<SomeChildren> someChildren;
@Id
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@OneToMany(fetch = FetchType.LAZY)
public Collection<SomeChildren> getSomeChildren() {
return someChildren;
}
public void setSomeChildren(Collection<SomeChildren> someCollection) {
this.someChildren = someCollection;
}
}
UPDATE: Why do I need this?
The full scenario is something like this: I have a MyEntity
instance which I got from a query with new
operator:
@SuppressWarnings({"unchecked"})
List<MyEntity> list = em.createQuery("select new MyEntity(...) from MyEntity e where ...").getResultList();
MyEntity original = list.get(0);
return original;
This works fine, but sometimes (and only sometimes) I need entity.getSomeChildren()
. Consequently I don't want to pass it to the constructor, so I thought of this trick:
MyEntity original = list.get(0);
MyEntity entity = em.getReference(MyEntity.class, original.getId());
Collection c = entity.getSomeChildren(); // prints "select ... from MyEntity where id=?"
original.setSomeChildren(c);
return original;
... hoping that it won't issue a database query, but it apparently does :(
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在查询中使用
select new MyEntity(...)
意味着您将 MyEntity 用作 DTO:一个包含实际 MyEntity 实例的某些字段的独立对象。如果您希望它引用子级的惰性集合,只需使用
select e from MyEntity e ...
:您将获得一个附加的 MyEntity 实例及其惰性(未初始化)子级集合。一旦调用子集合的任何方法,就会执行额外的查询来加载该集合。如果您坚持想要使用分离的 MyEntity,并且想要引用其子级列表,那么您确实必须重新加载实体(从而获取其附加版本),这当然会触发额外的 SQL查询只是为了再次加载实体及其所有字段。
我会避免使用 MyEntity 类作为 DTO,而是使用不包含子级列表的专用类。因此,调用者代码会清楚需要执行额外的临时 HQL 查询来获取子级列表。
Using
select new MyEntity(...)
in your query means that you're using your MyEntity as a DTO: a detached object containing some fields of the actual MyEntity instance.If you would like it to reference a lazy collection of children, just use
select e from MyEntity e ...
: you'll get an attached MyEntity instance, with its lazy (uninitialized) collection of children. And as soon as you call any method of the children collection, an additional query will be executed to load this collection.If you persist on wanting to use a detached MyEntity, and you want to have a reference to its list of children, you'll indeed have to reload the entity (and thus gets its attached version), which will of course trigger an additional SQL query just to laod the entity again, with all its fields.
I would avoid using the MyEntity class as a DTO, and use a dedicated class instead, not containing the list of children. It will thus be clear to the caller code that an additional, ad-hoc HQL query will need to be executed to get the list of children.