在运行时将FetchType.LAZY更改为FetchType.EAGER(Hibernate/JPA)

11
我有一个实体,其中一些字段被标记为LAZY。为了加载LAZY字段,只需在会话打开时访问getter方法,ORM代理就会执行select子查询。

但是,在需要完全解析的对象的情况下,使用与“eager”关系相同的策略(通过join获取),这种方式不太优化。

有什么办法可以将某些字段临时标记为EAGER仅用于一个查询吗?

这是否可能使用JPA标准实现,还是需要像Hibernate这样的专有扩展?

3个回答

8
Hibernate有一个名为Fetch Profiles的功能,可以解决这个问题。它需要访问Hibernate Session,但是您可以使用unwrap()EntityManager中访问它。
如果您想要一个纯JPA解决方案,在需要急切获取的用例中加载对象时,可以使用带有join fetch的查询。 更新: JPA 2.1(由Hibernate 4.3实现)支持类似于fetch profiles的功能-entity graphs

我正在尝试使用实体图和join fetch获取10个列表,但在两种情况下,我都会收到org.hibernate.loader.MultipleBagFetchException:无法同时获取多个bags的错误。这种情况是否有任何解决方法? - Nitika Bansal
避免多次使用Join Fetch,否则会导致MultipleBagFetchException。使用一个join fetch获取一个子列表,并使用此结果创建另一个join fetch以获取另一个子列表。 - abhinav kumar

3

Spring JpaRepository 允许使用 org.springframework.data.jpa.repository.EntityGraph 标记查询(包括自定义查询):

@Entity Book {
     @Id Long id;
     @OneToMany List<Author> authors;
}

@EntityGraph(attributePaths = {"authors", "author.address"})
@Query("select b from Book b" +
        " where b.id in (:ids)")
List<Book> loadAll(@Param("ids") List<Long> ids);

1
由于在关联的集合上使用fetch = FetchType.LAZY,遇到了问题,例如“org.hibernate.LazyInitializationException:无法初始化角色的集合:代理初始化失败-没有会话”。
现在将fetch = FetchType.LAZY更改为fetch = FetchType.EGAR是最糟糕的解决方案,因为它会引起其他问题。
然后我想,如果有一种方法可以在运行时将fetch类型从lazy更改为egar以进行某些操作……
然后我尝试理解问题,例如为什么我遇到了LazyInitializationException。
所以我意识到,这个问题发生是因为我们在关闭会话后执行数据库操作,因此我们必须在会话内进行所有数据库操作。
因此,解决方案是将在其中执行数据库操作的方法包装在事务中。请检查下面的代码片段->
 @Transactional
    public void fetchThroughLazy() {

        List<UserEntity> userList = aeqplUserRepository.findByEmailEndingWith("@amazon.com");
        userList.forEach(user->{
            logger.info("user --->{}",user);
            logger.info("user permission--->{}",user.getPermissions());
        });
    }

但是这并不是一个好的解决方案,因为它会导致Hibernate N+1问题。

最佳方案---> 使用EntityGraph或Join Fetch

请查看下面的代码片段 --->>

public void fetchThroughJPQLJoinFetch() {

            List<UserEntity> userList = entityManager.createQuery("select distinct u from UserEntity u left join fetch u.permissions Where u.email like :endsWith",UserEntity.class)
            .setParameter("endsWith", "%@google.com")
            .setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
            .getResultList();
        userList.forEach(user->{
            logger.info("user --->{}",user);
            logger.info("user permission--->{}",user.getPermissions());
        });
        
    }
    
    public void fetchThroughJPQLEntityGraph() {
        EntityGraph<UserEntity> userEntityGraph = em.createEntityGraph(UserEntity.class);
        userEntityGraph.addSubgraph("permissions");
        
        List<UserEntity> userList = em.createQuery("Select u From UserEntity u Where u.email Like :endsWith",UserEntity.class)
            .setParameter("endsWith", "%@facebook.com")
            .setHint(QueryHints.LOADGRAPH, userEntityGraph)
            .getResultList();
        userList.forEach(user->{
            logger.info("user --->{}",user);
            logger.info("user permission--->{}",user.getPermissions());
        });
    }


@Entity
@Table(name="user")
public class UserEntity {

    @Id
    @Column(name = "id")
    private Long id;
    
    @Column(name = "name")
    private String name;
    
    @Column(name = "email")
    private String email;
    
    @OneToMany(mappedBy="userEntity",cascade = CascadeType.All,fetch= FetchType.LAZY)
    private List<PermissionEntity> cmList =new ArrayList<>();

    //setters and getters
}

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

    @Id
    @Column(name = "id")
    private Long id;
    
    @Column(name = "name")
    private String name;
    
    @Column(name = "type")
    private String type;
    
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private UserEntity userEntity;

    //setters and getters
}

Note-> 避免使用Join Fetch超过一次,否则会引发MultipleBagFetchException异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接