【问题标题】:Hibernate @OneToOne loaded even though is lazyHibernate @OneToOne 加载即使是懒惰的
【发布时间】:2020-06-07 17:15:04
【问题描述】:

我正在使用 Spring Boot 2.3、Spring Data 和 Hibernate。

我有以下实体

@Entity
@Getter
@Setter
@EqualsAndHashCode(of = "id")
public class User {

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

    private String name;

    @OneToOne(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
    private Address address;

    @Version
    private Long version;
}

@Entity
@Getter
@Setter
@EqualsAndHashCode(of = "id")
public class Address {

    @Id
    private Long id;

    private String fullAddress;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "id")
    @MapsId
    private User user;

    @Version
    private Long version;    
}

执行以下代码时,将执行与用户存储库相关的任何查询(对我来说这是预期的行为)。

Address addressFromDb = addressRepository.findAll().get(0);
log.info("" + addressFromDb.getUser().getId());

// select address0_.id as id1_0_, address0_.full_address as full_add2_0_, address0_.version as version3_0_ from address address0_

但是当我执行以下代码时,会有多个查询,我不明白为什么。显然,从用户到地址的FetchType.LAZY 不被接受。

User userFromDb = userRepository.findAll().get(0);

// select user0_.id as id1_4_, user0_.name as name2_4_, user0_.version as version3_4_ from user user0_
// select address0_.id as id1_0_0_, address0_.full_address as full_add2_0_0_, address0_.version as version3_0_0_ from address address0_ where address0_.id=?

我错过了什么?

为了更有帮助和更清楚,我创建了以下github repo

【问题讨论】:

    标签: java spring hibernate spring-data hibernate-mapping


    【解决方案1】:

    Hibernate(或更具体地说是PersistenceContext)需要知道实体是否存在,以便它可以决定是为实体提供代理还是null。这不适用于XToMany 关系,因为整个集合可以包装在代理中,并且在特殊情况下它将为空。

    还需要指出的是,FetchType 只是对 JPa 实现的建议,不能保证在任何情况下都会实现。你可以阅读更多关于@OneToOnehere的信息,尤其是在获取策略方面:

    虽然单向@OneToOne 关联可以延迟获取,但双向@OneToOne 关联的父端却不能。即使指定关联不是可选的并且我们有 FetchType.LAZY,父端关联的行为也类似于 FetchType.EAGER 关系。而且 EAGER 抓取很糟糕。

    即使 FK 不是 NULL 并且父端通过可选属性(例如 @OneToOne(mappedBy = "post", fetch = FetchType.LAZY, optional = false))知道其不可为空性,Hibernate仍然会生成辅助 select 语句。

    对于每个托管实体,持久性上下文都需要实体类型和标识符, 所以在加载父实体时必须知道子标识符,而找到关联的 post_details 主键的唯一方法是执行二级查询。

    字节码增强是唯一可行的解​​决方法。但是,它仅在父端使用 @LazyToOne(LazyToOneOption.NO_PROXY) 注释且子端未使用 @MapsId 时才有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-29
      • 2016-08-17
      • 2012-03-04
      • 1970-01-01
      • 2011-01-13
      • 2016-10-07
      相关资源
      最近更新 更多