Java JPA Hibernate惰性获取属性和一个查询

Java JPA Hibernate惰性获取属性和一个查询,java,performance,hibernate,jpa,spring-data-jpa,Java,Performance,Hibernate,Jpa,Spring Data Jpa,考虑以下实体: 用户实体: 用户实体的角色应以惰性方式获取。问题是,当使用角色时(调用getter),hibernate会通过查询每个角色以及查询每个角色的每个权限来获取角色,从而导致n+1问题 因此,我的问题是:如何通过一个查询惰性地获取用户角色?我能否以某种方式利用RoleEntity的EntityGraph? 注意:我已尝试使用@Fetch: @Fetch(FetchMode.JOIN) @ManyToMany(fetch = FetchType.LAZY) @JoinTable(na

考虑以下实体:


用户实体:
用户实体
角色
应以惰性方式获取。问题是,当使用
角色时(调用getter),hibernate会通过查询每个角色以及查询每个角色的每个权限来获取角色,从而导致n+1问题

因此,我的问题是:如何通过一个查询惰性地获取用户角色?我能否以某种方式利用
RoleEntity
的EntityGraph?


注意:我已尝试使用
@Fetch

@Fetch(FetchMode.JOIN)
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "role_permission", joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "permission_id", referencedColumnName = "id"))
private List<PermissionEntity> permissions = new ArrayList<>();

不过,我对这个解决方案不满意。为什么我需要添加
@BatchSize

我认为使用子图可能会对您有所帮助。看看他的网站上的描述:,引用:

指定
@Fetch(FetchMode.JOIN)
将导致即时抓取(忽略
FetchType.LAZY

但是,
@Fetch(FetchMode.JOIN)

(使用时请注意)

我猜您使用的是查询,因此
@Fetch
被忽略了


@BatchSize
只影响延迟加载的集合,然后一次加载多个集合

例如,假设一个用户具有多个角色,每个角色都与多个权限关联,访问第一个角色的权限也会在同一查询中加载更多角色的权限

因此,当使用
@BatchSize
时,在访问用户所有角色的所有权限时,您仍应至少看到2个选择(第一个用于角色,第二个用于不同角色的权限)

另见:


为了能够仅为特定用例急切地获取用户的所有关联角色和权限,您可以选择使用
@EntityGraph
,例如,在这样的自定义存储库查询中:

@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {

  @EntityGraph(attributePaths = { "roles", "roles.permissions" })
  Optional<UserEntity> findWithPermissionsById(Long id);
}
@存储库
公共接口用户存储库扩展了JpaRepository{
@EntityGraph(AttributePath={“角色”,“角色.权限”})
可选findWithPermissionsById(长id);
}
这样,只有一条连接所有所需实体的大型select语句才能获取具有所有角色和所有权限的用户。
(为了避免混淆:find和ById之间的方法名中间部分是任意的,因此它也可以是
findFooById

使用带有子图的
@NamedEntityGraph
也可以获得类似的行为

@Entity(name = "permission")
public class PermissionEntity {

    @Id
    @Column(name = "id", columnDefinition = "serial")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;


}
@Fetch(FetchMode.JOIN)
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "role_permission", joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "permission_id", referencedColumnName = "id"))
private List<PermissionEntity> permissions = new ArrayList<>();
@Fetch(FetchMode.JOIN)
@BatchSize(size = 1000)
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "role_permission", joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "permission_id", referencedColumnName = "id"))
private List<PermissionEntity> permissions = new ArrayList<>();
@NamedEntityGraph(
    name = "graph.AuthorBooksPublisherEmployee", 
    attributeNodes = @NamedAttributeNode(value = "books", subgraph = "subgraph.book"), 
    subgraphs = {
        @NamedSubgraph(name = "subgraph.book", 
                       attributeNodes = @NamedAttributeNode(value = "publisher", subgraph = "subgraph.publisher")),
        @NamedSubgraph(name = "subgraph.publisher", 
                       attributeNodes = @NamedAttributeNode(value = "employees")) })
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {

  @EntityGraph(attributePaths = { "roles", "roles.permissions" })
  Optional<UserEntity> findWithPermissionsById(Long id);
}