休眠@OneToOne 加载即使是懒惰
Hibernate @OneToOne loaded even though is lazy
我正在使用 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
Hibernate(或更具体地说 PersistenceContext
)需要知道实体是否存在,以便它可以决定是否为实体提供代理或 null
。这不适用于 XToMany
关系,因为整个集合可以包装在代理中,在特殊情况下它将是空的。
同样重要的是要指出,FetchType
只是对 JPa 实施的建议,并不能保证在任何情况下都会实现。你可以阅读更多关于@OneToOne
here,特别是在抓取策略方面:
While the unidirectional @OneToOne association can be fetched lazily, the parent-side of a bidirectional @OneToOne association is not. Even when specifying that the association is not optional and we have the FetchType.LAZY, the parent-side association behaves like a FetchType.EAGER relationship. And EAGER fetching is bad.
Even if the FK is NOT NULL and the parent-side is aware about its non-nullability through the optional attribute (e.g. @OneToOne(mappedBy = "post", fetch = FetchType.LAZY, optional = false)), Hibernate still generates a secondary select statement.
For every managed entity, the Persistence Context requires both the entity type and the identifier,
so the child identifier must be known when loading the parent entity, and the only way to find the associated post_details primary key is to execute a secondary query.
Bytecode enhancement is the only viable workaround. However, it only works if the parent side is annotated with @LazyToOne(LazyToOneOption.NO_PROXY) and the child side is not using @MapsId.
我正在使用 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
Hibernate(或更具体地说 PersistenceContext
)需要知道实体是否存在,以便它可以决定是否为实体提供代理或 null
。这不适用于 XToMany
关系,因为整个集合可以包装在代理中,在特殊情况下它将是空的。
同样重要的是要指出,FetchType
只是对 JPa 实施的建议,并不能保证在任何情况下都会实现。你可以阅读更多关于@OneToOne
here,特别是在抓取策略方面:
While the unidirectional @OneToOne association can be fetched lazily, the parent-side of a bidirectional @OneToOne association is not. Even when specifying that the association is not optional and we have the FetchType.LAZY, the parent-side association behaves like a FetchType.EAGER relationship. And EAGER fetching is bad.
Even if the FK is NOT NULL and the parent-side is aware about its non-nullability through the optional attribute (e.g. @OneToOne(mappedBy = "post", fetch = FetchType.LAZY, optional = false)), Hibernate still generates a secondary select statement.
For every managed entity, the Persistence Context requires both the entity type and the identifier, so the child identifier must be known when loading the parent entity, and the only way to find the associated post_details primary key is to execute a secondary query.
Bytecode enhancement is the only viable workaround. However, it only works if the parent side is annotated with @LazyToOne(LazyToOneOption.NO_PROXY) and the child side is not using @MapsId.