@EntityGraph/JOIN FETCH 不支持字节码增强
@EntityGraph/JOIN FETCH are not working with Bytecode Enanhcement
我正在使用字节码增强通过基本注解延迟加载用户实体的头像属性;我还想延迟加载与成员实体(扩展用户)相关的身份证。
到目前为止一切正常,我的代码如下:
用户class:
@Data
@Entity
@Table(name = "users")
@Inheritance(strategy = InheritanceType.JOINED)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotEmpty
private String name;
@NotEmpty
private String surname;
@Lob
@NotNull
@Basic(fetch = FetchType.LAZY)
private byte[] avatar;
}
会员class:
@Data
@Entity
@Table(name = "members")
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Member extends User {
@NotNull
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@LazyToOne(value = LazyToOneOption.NO_PROXY)
private IdentityCard identityCard;
}
身份证class:
@Data
@Entity
@Table(name = "identityCards")
public class IdentityCard {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Lob
@NotNull
private byte[] backImage;
@Lob
@NotNull
private byte[] frontImage;
}
pom.xml
<plugins>
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<executions>
<execution>
<configuration>
<failOnError>true</failOnError>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
application.yml:
...
spring:
jpa:
open-in-view: false
...
现在,在我的控制器中,我想通过存储库中的 EntityGraph 注释获取会员的头像和身份证
控制器:
@Controller
@RequestMapping("members")
public class MemberController {
@Autowired
MemberService memberService;
@GetMapping("/{id}")
public String get(@PathVariable String id, Model model) {
Optional<Member> member = memberService.findFullMemberById(Long.valueOf(id));
// Using model to pass member and let thymeleaf process it
return "members/form";
}
}
会员资料库:
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
...
@EntityGraph(attributePaths = {"avatar", "identityCard"})
Optional<Member> findFullMemberById(Long id);
}
但是我得到的Member 对象总是将avatar 和identityCard 属性都设置为null
。
我也尝试使用 Query 注释和 LEFT JOIN FETCH 得到相同的结果。
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
...
@Query("select m from Member m LEFT JOIN FETCH m.identityCard identityCard where m.id=:id")
Optional<Member> findFullMemberById(Long id);
}
- Spring 开机:2.2.5.FINAL
- 休眠:5.4.12.FINAL
感谢您的帮助。谢谢。
从 Hibernate 5.4.26 开始,有 2 个明显的问题。
1。懒人基本属性
您正在尝试使用 @EntityGraph
急切地获取惰性基本属性,这在 Hibernate 5 中不受支持,但是 Hibernate 6 有一个功能请求允许 EntityGraphs 控制基本实体状态的加载(不仅是协会):https://hibernate.atlassian.net/browse/HHH-9270
我不知道有没有解决方法,我认为它不存在。 enableLazyInitialization
的替代方法是使用 子实体 或更好的 投影 ,如 https://vladmihalcea.com/the-best-way-to-lazy-load-entity-attributes-using-jpa-and-hibernate[= 中所述44=]
2。 ToOne 协会
确实,enableLazyInitialization
允许延迟加载 @OneToOne
和 @LazyToOne(NO_PROXY)
,但是 JOIN FETCH 变得更难:您必须在会话打开时初始化关联才能在会话外使用它,它仍然会产生 N+1 SELECT!
初始加载计划中排除了所有惰性单数属性(包括惰性 ToOne
关联的连接列)。
没有join fetch
当通过 id 查找成员时,Hibernate 将生成:
- 先SELECT获得急切属性:
id,name,surname
- 需要的时候,一个N+1SELECT去读取对应隐含的惰性属性
@LazyGroup
:avatar,identity_card_id
- a N+1 SELECT 获取关联的
identityCard
加入fetch
当连接和获取 identityCard 时,Hibernate 生成正确的 SQL JOIN 来获取关联的 identityCard,但它错过了 member.identity_card_id
连接列,导致 LazyInitializationException(我没有一个 null,如您的问题中所述,但完全相同)。
- 首先SELECT获取eager属性
id,name,surname
和加入的实体属性:identityCard.id,identityCard.frontImage,identityCard.backImage
- 需要的时候,一个N+1SELECT读取对应隐式
@LazyGroup
的惰性属性:avatar,identity_card_id
(和没有join fetch一样! )
存在与此错误(或功能...)相关的未解决问题:
https://hibernate.atlassian.net/browse/HHH-13203 启用字节码增强后,用于标准查询的提取似乎被忽略。
https://hibernate.atlassian.net/browse/HHH-13134 JOIN FETCH 不能与增强实体一起正常工作
2.1。修复
启用这个新选项:
hibernate.bytecode.allow_enhancement_as_proxy=true
您将获得与未启用字节码增强和 enableLazyInitialization 相同的行为,并且 JOIN FETCH 将按预期工作,但您仍然能够延迟加载基本属性和 @OneToOne
。
这样,您甚至不需要在所有惰性 ToOne
.
上添加 @LazyToOne(NO_PROXY)
(@OneToOne
除外)和 @LazyGroup
请参阅我对此问题的评论。
https://hibernate.atlassian.net/browse/HHH-13134?focusedCommentId=107604
有关此新功能的更多信息,请查看
https://in.relation.to/2019/07/30/bytecode-proxy/
另一个建议
当不使用新的 hibernate.bytecode.allow_enhancement_as_proxy
或 @LazyGroup
时,所有惰性属性都隐含地属于同一组。因此,当延迟加载 identityCard
时,Hibernate 也会加载 avatar
,这可能不是您想要的。
所以你应该把lob和身份证用@LazyGroup("lob")
和@LazyGroup("identityCard")
分开或者用hibernate.bytecode.allow_enhancement_as_proxy
.
我正在使用字节码增强通过基本注解延迟加载用户实体的头像属性;我还想延迟加载与成员实体(扩展用户)相关的身份证。 到目前为止一切正常,我的代码如下:
用户class:
@Data
@Entity
@Table(name = "users")
@Inheritance(strategy = InheritanceType.JOINED)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotEmpty
private String name;
@NotEmpty
private String surname;
@Lob
@NotNull
@Basic(fetch = FetchType.LAZY)
private byte[] avatar;
}
会员class:
@Data
@Entity
@Table(name = "members")
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Member extends User {
@NotNull
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@LazyToOne(value = LazyToOneOption.NO_PROXY)
private IdentityCard identityCard;
}
身份证class:
@Data
@Entity
@Table(name = "identityCards")
public class IdentityCard {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Lob
@NotNull
private byte[] backImage;
@Lob
@NotNull
private byte[] frontImage;
}
pom.xml
<plugins>
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<executions>
<execution>
<configuration>
<failOnError>true</failOnError>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
application.yml:
...
spring:
jpa:
open-in-view: false
...
现在,在我的控制器中,我想通过存储库中的 EntityGraph 注释获取会员的头像和身份证
控制器:
@Controller
@RequestMapping("members")
public class MemberController {
@Autowired
MemberService memberService;
@GetMapping("/{id}")
public String get(@PathVariable String id, Model model) {
Optional<Member> member = memberService.findFullMemberById(Long.valueOf(id));
// Using model to pass member and let thymeleaf process it
return "members/form";
}
}
会员资料库:
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
...
@EntityGraph(attributePaths = {"avatar", "identityCard"})
Optional<Member> findFullMemberById(Long id);
}
但是我得到的Member 对象总是将avatar 和identityCard 属性都设置为null
。
我也尝试使用 Query 注释和 LEFT JOIN FETCH 得到相同的结果。
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
...
@Query("select m from Member m LEFT JOIN FETCH m.identityCard identityCard where m.id=:id")
Optional<Member> findFullMemberById(Long id);
}
- Spring 开机:2.2.5.FINAL
- 休眠:5.4.12.FINAL
感谢您的帮助。谢谢。
从 Hibernate 5.4.26 开始,有 2 个明显的问题。
1。懒人基本属性
您正在尝试使用 @EntityGraph
急切地获取惰性基本属性,这在 Hibernate 5 中不受支持,但是 Hibernate 6 有一个功能请求允许 EntityGraphs 控制基本实体状态的加载(不仅是协会):https://hibernate.atlassian.net/browse/HHH-9270
我不知道有没有解决方法,我认为它不存在。 enableLazyInitialization
的替代方法是使用 子实体 或更好的 投影 ,如 https://vladmihalcea.com/the-best-way-to-lazy-load-entity-attributes-using-jpa-and-hibernate[= 中所述44=]
2。 ToOne 协会
确实,enableLazyInitialization
允许延迟加载 @OneToOne
和 @LazyToOne(NO_PROXY)
,但是 JOIN FETCH 变得更难:您必须在会话打开时初始化关联才能在会话外使用它,它仍然会产生 N+1 SELECT!
初始加载计划中排除了所有惰性单数属性(包括惰性 ToOne
关联的连接列)。
没有join fetch
当通过 id 查找成员时,Hibernate 将生成:
- 先SELECT获得急切属性:
id,name,surname
- 需要的时候,一个N+1SELECT去读取对应隐含的惰性属性
@LazyGroup
:avatar,identity_card_id
- a N+1 SELECT 获取关联的
identityCard
加入fetch
当连接和获取 identityCard 时,Hibernate 生成正确的 SQL JOIN 来获取关联的 identityCard,但它错过了 member.identity_card_id
连接列,导致 LazyInitializationException(我没有一个 null,如您的问题中所述,但完全相同)。
- 首先SELECT获取eager属性
id,name,surname
和加入的实体属性:identityCard.id,identityCard.frontImage,identityCard.backImage
- 需要的时候,一个N+1SELECT读取对应隐式
@LazyGroup
的惰性属性:avatar,identity_card_id
(和没有join fetch一样! )
存在与此错误(或功能...)相关的未解决问题:
https://hibernate.atlassian.net/browse/HHH-13203 启用字节码增强后,用于标准查询的提取似乎被忽略。
https://hibernate.atlassian.net/browse/HHH-13134 JOIN FETCH 不能与增强实体一起正常工作
2.1。修复
启用这个新选项:
hibernate.bytecode.allow_enhancement_as_proxy=true
您将获得与未启用字节码增强和 enableLazyInitialization 相同的行为,并且 JOIN FETCH 将按预期工作,但您仍然能够延迟加载基本属性和 @OneToOne
。
这样,您甚至不需要在所有惰性 ToOne
.
@LazyToOne(NO_PROXY)
(@OneToOne
除外)和 @LazyGroup
请参阅我对此问题的评论。 https://hibernate.atlassian.net/browse/HHH-13134?focusedCommentId=107604
有关此新功能的更多信息,请查看 https://in.relation.to/2019/07/30/bytecode-proxy/
另一个建议
当不使用新的 hibernate.bytecode.allow_enhancement_as_proxy
或 @LazyGroup
时,所有惰性属性都隐含地属于同一组。因此,当延迟加载 identityCard
时,Hibernate 也会加载 avatar
,这可能不是您想要的。
所以你应该把lob和身份证用@LazyGroup("lob")
和@LazyGroup("identityCard")
分开或者用hibernate.bytecode.allow_enhancement_as_proxy
.