JPA/Hibernate:@ManyToOne 和 @OneToOne 关系标记为 FetchType.LAZY 且可选 = false 未在 em.find() 上延迟加载?

发布于 2024-12-09 11:38:53 字数 6417 浏览 0 评论 0原文

我有以下实体(仅显示相关映射):

@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 关系(如网络上许多地方所述)。我主要查看的来源是:

使 OneToOne-relation 变得懒惰

如前所述在那里,不应获取 @ManyToOne 关系 User user

@ManyToOne(fetch=FetchType.LAZY) 应该可以正常工作。

...这里是从PQUser的关系,但是它提取的,正如您从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(...) 中使用的实体。 (如果这还不够,请启发我。)

我现在的问题有两个:

  1. 为什么急切地获取 User 实体的 @ManyToOne (假设是据说是懒惰地工作,请参阅使 OneToOne 关系变得懒惰)?
  2. 为什么只提取与 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:

  1. Why is the @ManyToOne to the User entity fetched eagerly (given that is was said to be working lazily, see Making a OneToOne-relation lazy)?
  2. Why is only the OneToOne relationship to the Tendering entity left off being fetched? Is it because the Tendering entity references the PQ's PK column as a PK itself (@Id in Tendering), which the Group 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

相对绾红妆 2024-12-16 11:38:53

使用 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

你的心境我的脸 2024-12-16 11:38:53

您好,我不确定 JPA,但对于 多对一一对一 映射,hibernate 支持的惰性值是 proxy、no-proxyfalse 其中默认为 false。检查DTD的这一部分

lazy="proxy|no-proxy|false"

你可以检查这个链接。我认为这在很大程度上回答了你的第一个问题。

Hi I am not sure about JPA, but for many-to-one and one-to-one mappings the lazy values that are supported hibernate are proxy, no-proxy and false out of which false is defaulted. check this part of the DTD

lazy="proxy|no-proxy|false"

and you can check this Link. I think this answers your first question to an extend.

隐诗 2024-12-16 11:38:53

*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?

罗罗贝儿 2024-12-16 11:38:53

您好 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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文