JPA/Hibernate:@ManyToOne 和 @OneToOne 关系标记为 FetchType.LAZY 且可选 = false 未在 em.find() 上延迟加载?
我有以下实体(仅显示相关映射):
@Entity
@Table(name = "PQs")
public class PQ implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer id;
@Column
private String name;
@ManyToOne(fetch = FetchType.LAZY) // lazy XToOne
@JoinColumn(name = "user_id", referencedColumnName = "person_id")
private User user;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY) // lazy XToOne
private Group group;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY) // lazy XToOne
private Tendering tendering;
...
}
请注意上面的注释:与其他实体存在三个 @XToOne
关系:
User(一个 SecurityIdentity 子类,带有一个简单的ID 为 PK,由代表所有者的 PQ 引用):
@Entity
@Table(name = "Users")
@DiscriminatorValue(value = "user")
public class User extends SecurityIdentity
{
@Column
private String name;
@OneToMany(mappedBy = "user")
private Set<PQ> pqs = new HashSet<PQ>();
...
}
Group(也是一个 SecurityIdentity 子类,其简单 ID 为 PK,引用 PQ 来代表可以与该 PQ 交互的一组用户):
@Entity
@Table(name = "Groups")
@DiscriminatorValue(value = "group")
public class Group extends SecurityIdentity
{
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pq_id", referencedColumnName = "id")
private PQ pq;
...
}
招标:
@Entity
@Table(name = "Tenderings")
public class Tendering implements Serializable
{
@Id
@Column(name = "pq_id", insertable = false, updatable = false)
private Integer pqId;
@Column(name = "external_code")
private String externalCode;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pq_id", referencedColumnName = "id")
private PQ pq;
...
}
不要对共享ID的组和用户感到困惑,只需将它们视为简单的ID即可。招标只是一个单独的文档对象(一对一)。
正如您所看到的,PQ 实体上存在三个 @XToOne
关系,如果未设置获取类型,则会立即加载(JPA 默认值)。因此,为了防止这种情况,我将所有 @XToOne
关系标记为 FetchType.LAZY
。
现在,当使用
em.find(PQ.class, someExistingId);
时,我得到 Hibernate 输出:
23:53:55,815 INFO [stdout] Hibernate: select pq0_.id as id291_0_, pq0_.description as descript2_291_0_, pq0_.name as name291_0_, pq0_.submission_date as submission4_291_0_, pq0_.user_id as user5_291_0_ from PQs pq0_ where pq0_.id=?
23:53:55,818 INFO [stdout] Hibernate: select user0_.id as id280_0_, user0_1_.identity_type_id as identity2_280_0_, user0_.is_enabled as is1_297_0_, user0_.name as name297_0_, user0_.password as password297_0_, user0_.person_id as person5_297_0_ from Users user0_ inner join SecurityIdentities user0_1_ on user0_.id=user0_1_.id where user0_.person_id=?
23:53:55,821 INFO [stdout] Hibernate: select group0_.id as id280_0_, group0_1_.identity_type_id as identity2_280_0_, group0_.pq_id as pq2_281_0_ from Groups group0_ inner join SecurityIdentities group0_1_ on group0_.id=group0_1_.id where group0_.pq_id=?
23:53:55,823 INFO [stdout] Hibernate: select tendering0_.pq_id as pq1_296_0_, tendering0_.binary_file as binary2_296_0_, tendering0_.customer_id as customer6_296_0_, tendering0_.description as descript3_296_0_, tendering0_.external_code as external4_296_0_, tendering0_.title as title296_0_ from Tenderings tendering0_ where tendering0_.pq_id=?
三个额外的 SELECT 源于 @XToOne 关系(如网络上许多地方所述)。我主要查看的来源是:
如前所述在那里,不应获取 @ManyToOne
关系 User user
:
@ManyToOne(fetch=FetchType.LAZY)
应该可以正常工作。
...这里是从PQ到User的关系,但是它是提取的,正如您从select user0_.id as中看到的那样id280_0_, ...
声明...
对于另外两个Group组
和Tendering招标
,都是@OneToOne
反向映射,外键引用PQs表的PK(ID),导致PQ实体中的映射相同。
请注意,所有三种关系都不是可选的:PQ 始终具有所有者(用户),并且 PQ 始终由招标和组实体引用。我只是还没有在上面的 JPA 中对此进行建模...
因此,当将 Optional = false
添加到 PQ 实体的三个关系时:
@Entity
@Table(name = "PQs")
public class PQ implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer id;
@Column
private String name;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", referencedColumnName = "person_id")
private User user;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY, optional = false)
private Group group;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY, optional = false)
private Tendering tendering;
...
}
...我得到以下 Hibernate 输出:
00:47:34,397 INFO [stdout] Hibernate: select pq0_.id as id361_0_, pq0_.description as descript2_361_0_, pq0_.name as name361_0_, pq0_.submission_date as submission4_361_0_, pq0_.user_id as user5_361_0_ from PQs pq0_ where pq0_.id=?
00:47:34,410 INFO [stdout] Hibernate: select user0_.id as id350_0_, user0_1_.identity_type_id as identity2_350_0_, user0_.is_enabled as is1_367_0_, user0_.name as name367_0_, user0_.password as password367_0_, user0_.person_id as person5_367_0_ from Users user0_ inner join SecurityIdentities user0_1_ on user0_.id=user0_1_.id where user0_.person_id=?
00:47:34,413 INFO [stdout] Hibernate: select group0_.id as id350_0_, group0_1_.identity_type_id as identity2_350_0_, group0_.pq_id as pq2_351_0_ from Groups group0_ inner join SecurityIdentities group0_1_ on group0_.id=group0_1_.id where group0_.pq_id=?
注意,我只在 PQ 实体上使用过 optional = false
,因为这是我在 em.find(...)
中使用的实体。 (如果这还不够,请启发我。)
我现在的问题有两个:
- 为什么急切地获取
User
实体的@ManyToOne
(假设是据说是懒惰地工作,请参阅使 OneToOne 关系变得懒惰)? - 为什么只提取与
Tendering
实体的OneToOne
关系?是否因为Tendering
实体引用 PQ 的 PK 列作为 PK 本身(Tendering
中的@Id
),而Group< /code>实体没有(与PQ的PK有规律的关系)?
怎么了?如何使这些非可选关系变得懒惰? (没有代码检测或其他黑客,只是简单的注释...)
我知道 LAZY 的事情只是 JPA 提供者是否执行有关延迟加载的操作的提示,但在这种情况下,似乎还有其他问题(作为它的一部分正在工作)。
PS:我使用的是 Hibernate 4.0 BETA,该版本随 JBoss 7.0.0.Final 一起提供,仅带有 JPA 注释(以上均与 JPA 1.0 兼容)。
I have the following entity (only relevant mappings shown):
@Entity
@Table(name = "PQs")
public class PQ implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer id;
@Column
private String name;
@ManyToOne(fetch = FetchType.LAZY) // lazy XToOne
@JoinColumn(name = "user_id", referencedColumnName = "person_id")
private User user;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY) // lazy XToOne
private Group group;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY) // lazy XToOne
private Tendering tendering;
...
}
Note the comments above: there are three @XToOne
relationships to other entities:
User (a SecurityIdentity sub class with a simple ID as PK, referenced by PQ representing the owner):
@Entity
@Table(name = "Users")
@DiscriminatorValue(value = "user")
public class User extends SecurityIdentity
{
@Column
private String name;
@OneToMany(mappedBy = "user")
private Set<PQ> pqs = new HashSet<PQ>();
...
}
Group (also a SecurityIdentity sub class with a simple ID as PK, references the PQ to represent a set of users that can interact with that PQ):
@Entity
@Table(name = "Groups")
@DiscriminatorValue(value = "group")
public class Group extends SecurityIdentity
{
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pq_id", referencedColumnName = "id")
private PQ pq;
...
}
Tendering:
@Entity
@Table(name = "Tenderings")
public class Tendering implements Serializable
{
@Id
@Column(name = "pq_id", insertable = false, updatable = false)
private Integer pqId;
@Column(name = "external_code")
private String externalCode;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "pq_id", referencedColumnName = "id")
private PQ pq;
...
}
Don't be confused about groups and users sharing IDs, just treat them as simple IDs. A tendering is just a separate document object (one-to-one).
As you can see there are three @XToOne
relationships on the PQ entity, which, if no fetch type was set, would be loaded eagerly (JPA default). So to prevent this I tagged all @XToOne
relationships as FetchType.LAZY
.
Now when using
em.find(PQ.class, someExistingId);
I get the Hibernate output:
23:53:55,815 INFO [stdout] Hibernate: select pq0_.id as id291_0_, pq0_.description as descript2_291_0_, pq0_.name as name291_0_, pq0_.submission_date as submission4_291_0_, pq0_.user_id as user5_291_0_ from PQs pq0_ where pq0_.id=?
23:53:55,818 INFO [stdout] Hibernate: select user0_.id as id280_0_, user0_1_.identity_type_id as identity2_280_0_, user0_.is_enabled as is1_297_0_, user0_.name as name297_0_, user0_.password as password297_0_, user0_.person_id as person5_297_0_ from Users user0_ inner join SecurityIdentities user0_1_ on user0_.id=user0_1_.id where user0_.person_id=?
23:53:55,821 INFO [stdout] Hibernate: select group0_.id as id280_0_, group0_1_.identity_type_id as identity2_280_0_, group0_.pq_id as pq2_281_0_ from Groups group0_ inner join SecurityIdentities group0_1_ on group0_.id=group0_1_.id where group0_.pq_id=?
23:53:55,823 INFO [stdout] Hibernate: select tendering0_.pq_id as pq1_296_0_, tendering0_.binary_file as binary2_296_0_, tendering0_.customer_id as customer6_296_0_, tendering0_.description as descript3_296_0_, tendering0_.external_code as external4_296_0_, tendering0_.title as title296_0_ from Tenderings tendering0_ where tendering0_.pq_id=?
The three extra SELECTs stem from the @XToOne relationships (as described in many places on the net). The source I was looking at mostly is this:
Making a OneToOne-relation lazy
As mentioned there, the @ManyToOne
relationship User user
shouldn't be fetched:
@ManyToOne(fetch=FetchType.LAZY)
should work just fine.
... here the relationship from PQ to User, but it is fetched as you can see from the select user0_.id as id280_0_, ...
statement...
For the other two Group group
and Tendering tendering
, both @OneToOne
reverse mappings, the foreign keys reference the PQs table's PK (ID), resulting in the same mapping in the PQ entity.
Note that all three relationships aren't optional: a PQ always has an owner (user), and a PQ is always referenced by a tendering and a group entity. I just hadn't modeled that in JPA above yet...
So, when adding optional = false
to the three relationships of the PQ entity:
@Entity
@Table(name = "PQs")
public class PQ implements Serializable
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Integer id;
@Column
private String name;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", referencedColumnName = "person_id")
private User user;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY, optional = false)
private Group group;
@OneToOne(mappedBy = "pq", fetch = FetchType.LAZY, optional = false)
private Tendering tendering;
...
}
... I get the following Hibernate output:
00:47:34,397 INFO [stdout] Hibernate: select pq0_.id as id361_0_, pq0_.description as descript2_361_0_, pq0_.name as name361_0_, pq0_.submission_date as submission4_361_0_, pq0_.user_id as user5_361_0_ from PQs pq0_ where pq0_.id=?
00:47:34,410 INFO [stdout] Hibernate: select user0_.id as id350_0_, user0_1_.identity_type_id as identity2_350_0_, user0_.is_enabled as is1_367_0_, user0_.name as name367_0_, user0_.password as password367_0_, user0_.person_id as person5_367_0_ from Users user0_ inner join SecurityIdentities user0_1_ on user0_.id=user0_1_.id where user0_.person_id=?
00:47:34,413 INFO [stdout] Hibernate: select group0_.id as id350_0_, group0_1_.identity_type_id as identity2_350_0_, group0_.pq_id as pq2_351_0_ from Groups group0_ inner join SecurityIdentities group0_1_ on group0_.id=group0_1_.id where group0_.pq_id=?
Note, that I've only been playing with the optional = false
on the PQ entity, as this is the one I use in em.find(...)
. (If this is not sufficient, please enlighten me.)
My question now is two-fold:
- Why is the
@ManyToOne
to theUser
entity fetched eagerly (given that is was said to be working lazily, see Making a OneToOne-relation lazy)? - Why is only the
OneToOne
relationship to theTendering
entity left off being fetched? Is it because theTendering
entity references the PQ's PK column as a PK itself (@Id
inTendering
), which theGroup
entity doesn't (regular relationship to the PQ's PK)?
What's wrong? How do I make these non-optional relationships lazy? (without code-instrumentation or other hacks, just plain annotations...)
I know the LAZY thing is just a hint for the JPA provider to do something about lazy loading or not, but in this case it seems as if something else is wrong (as part of it is working).
PS: I'm using Hibernate 4.0 BETA, the version that comes with JBoss 7.0.0.Final along with JPA annotations only (the above are all JPA 1.0 compatible).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用 hibernate 注释和 jpa 注释之间存在差异,据我所知,除了某些情况外,hibernate 默认情况下是延迟加载的。这是一个快速讨论:
http://community.jboss.org/wiki/AShortPrimerOnFetchingStrategies
there are differences between using hibernate annotations and jpa annotations and as far as i know, hibernate is lazy loaded by default except in some cases. here's a quick discussion:
http://community.jboss.org/wiki/AShortPrimerOnFetchingStrategies
您好,我不确定 JPA,但对于
多对一
和一对一
映射,hibernate 支持的惰性值是proxy、
no-proxy
和false
其中默认为 false。检查DTD的这一部分lazy="proxy|no-proxy|false"
你可以检查这个链接。我认为这在很大程度上回答了你的第一个问题。
Hi I am not sure about JPA, but for
many-to-one
andone-to-one
mappings the lazy values that are supported hibernate areproxy
,no-proxy
andfalse
out of which false is defaulted. check this part of the DTDlazy="proxy|no-proxy|false"
and you can check this Link. I think this answers your first question to an extend.
*ToOne关系意味着对象初始化后必须有(代理)bean,一旦以任何方式访问它就会触发选择(您正在看到它)您确定您对可能强制加载的对象不执行任何操作吗?
*ToOne relationship implies that there must be (proxy) bean after object initialisation, which will trigger select (you are seeing it) as soon as it is accessed in any way are you sure you do nothing with objects that could force loading?
您好 Kawu,您显示了额外的 SELECT,因为它将对所有涉及的实体执行 SELECT,尝试使用 FetchType.LAZY 进行默认的一对多,并且在大多数情况下使用 FETCH JOIN 来获取结果。
我希望对您或需要此信息的人有所帮助
Hi Kawu you show extra SELECTs because it will execute SELECTs for all involved entities, try using FetchType.LAZY for default One-to-Many and in most cases so using FETCH JOIN to get results.
I hope to help u or someone that need this information