spring - 当我尝试使用 JPA EntityGraph 使用 2 个集合加载实体时,MultipleBagFetchException

标签 spring hibernate jpa spring-data-jpa entitygraph

我有用户实体:

@ToString
@Data
@Entity
@Table(name = "users")
@NamedEntityGraph(name = "UserWithItems",
        attributeNodes = {
                @NamedAttributeNode("items"),
                @NamedAttributeNode("roles")
        })
public class User {

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

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

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Item> items;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Role> roles;
}

项目:

@ToString(exclude = "user")
@Data
@Entity
@Table(name = "items")
public class Item {

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

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

    @ManyToOne
    @JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)
    private User user;

}

作用:

@ToString
@Data
@Entity
@Table(name = "roles")
public class Role {

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

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

    @ManyToOne
    @JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)
    private User user;
}

我想为用户加载项目和角色。我使用 @NamedEntityGraph。这是我的存储库:

@EntityGraph(value = "UserWithItems", type = EntityGraph.EntityGraphType.LOAD)
@Query("select u from User u where u.id = ?1 and u.name =?2")
User getOneById(Long id, String name);

但是我得到一个错误:

Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [com.example.egerload.entity.User.roles, com.example.egerload.entity.User.items]
    at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:75) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
    at org.hibernate.loader.hql.QueryLoader.<init>(QueryLoader.java:108) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:212) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:143) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:119) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:85) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.makeQueryParametersForExecution(AbstractProducedQuery.java:1350) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1539) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1505) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
    ... 41 common frames omitted

最佳答案

您可以将“UserWithItems” @NamedEntityGraph 拆分为两个@NamedEntityGraph,从而产生两个查询,如Hibernate throws MultipleBagFetchException - cannot simultaneously fetch multiple bags - answer of Vlad Mihalcea 中所述。 .

用户

@ToString
@Data
@Entity
@Table(name = "users")
@NamedEntityGraphs(
    {
            @NamedEntityGraph(
                    name = "UserWithItems",
                    attributeNodes = {
                            @NamedAttributeNode("items")
                    }
            ),
            @NamedEntityGraph(
                    name = "UserWithRoles",
                    attributeNodes = {
                            @NamedAttributeNode("roles")
                    }
            ),
    }
)
public class User {
    ...
}

我假设您有一个存储库类。例如 extends JpaRepository。在额外的方法上使用每个 NamedEntityGraph。 (我省略了 name 条件和 @Query("...")id 条件应该足够了,因为它是用户的标识符。不需要@Query("...")。)

用户库

public interface UserRepository extends JpaRepository<User, Long> {

    @EntityGraph(value = "UserWithItems", type = EntityGraph.EntityGraphType.LOAD)
    Optional<User> getOneWithItemsById(Long id);

    @EntityGraph(value = "UserWithRoles", type = EntityGraph.EntityGraphType.LOAD)
    Optional<User> getOneWithRolesById(Long id);

    ....
}

最后,您可以在服务中同时调用这两种方法。

用户服务

public interface UserService {
    Optional<User> readById(Long id);
}

用户服务实现

@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    @Transactional
    public Optional<User> readById(Long id) {
        // Load user with items into persistence contex
        userRepository.getOneWithItemsById(id);
        // Load user with roles into persistence context 
        // (There is only one user instance by id within the persistence context)
        return userRepository.getOneWithRolesById(id);
    }

}

关于spring - 当我尝试使用 JPA EntityGraph 使用 2 个集合加载实体时,MultipleBagFetchException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54885910/

相关文章:

java - 如何遍历 JPA 响应并从中获取值

java - Spring Boot 和 JPA+Postgres : Lob mapped to Text but integer is persisted

java - 使用非主键列的实体映射

java - Hibernate 级联删除未按预期工作

java - 没有为 spring boot 元素加载静态 css

java - Spring RabbitMQ SimpleRabbitListenerContainerFactory 用法

hibernate - 在 hibernate 应用程序中设置字节码提供程序的问题

java - 在 hibernate 中检索子属性时出错

java - Spring Data JPA 是否需要 Spring Boot?

java - 使用 JNI 在 C++ 中加载 .jar 文件