【问题标题】:Hibernate - lazy initialize failureHibernate - 延迟初始化失败
【发布时间】:2021-08-12 13:42:47
【问题描述】:

我正在尝试使用休眠模式从数据库中加载实体,但出现以下错误

failed to lazily initialize a collection of role: package.entities.machinegroup.MachineGroup.users

这是我的 MachineGroup 实体:

public class MachineGroup {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO, generator = "machine_groups_seq")
    @SequenceGenerator(name = "machine_groups_seq", allocationSize = 1, initialValue = 2)
    @Column(name = "id")
    private long id;
    @Column(name = "name")
    private String name;
    @Column(name = "creation_time")
    private Date creationTime;
    @Column(name = "is_official")
    private boolean official;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "machine_properties_id", nullable = false)
    private ContinuousIntegrationProperties defaultContinuousIntegrationProperties;
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "machine_groups_to_users",
            joinColumns = @JoinColumn(name = "machine_group_id"),
            inverseJoinColumns = @JoinColumn(name = "user_id"))
    private Set<User> users = new HashSet<>();
 @JoinTable(name = "machine_groups_to_versions",
            joinColumns = @JoinColumn(name = "machine_group_id"),
            inverseJoinColumns = @JoinColumn(name = "version_id"))
    private Set<Version> versions = new HashSet<>();
}

这是我要获取的实体:

public class MachineGroupToVersion {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO, generator = "machine_groups_to_versions_seq")
    @SequenceGenerator(name = "machine_groups_to_versions_seq", allocationSize = 1)
    @Column(name = "id")
    private long id;
    @ManyToOne
    @JoinColumn(name = "machine_group_id", nullable = false)
    private MachineGroup machineGroup;
    @ManyToOne
    @JoinColumn(name = "version_id", nullable = false)
    private Version version;
}

如果我明确说“fetch = FetchType.EAGER”,为什么它会说它无法延迟初始化 MachinGroup 中的集合用户?

更新:

用户类

public class User {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO, generator = "users_seq")
    @SequenceGenerator(name = "users_seq", allocationSize = 1, initialValue = 2)
    @Column(name = "ID")
    private long id;
    @Column(name = "username")
    private String username;
    @Column(name = "creation_time")
    private Date creationTime;
    @Column(name = "role")
    private String role;
    @Column(name = "email")
    private String email;
    @ManyToMany(mappedBy = "users", cascade = CascadeType.MERGE)
    private Set<MachineGroup> machineGroups = new HashSet<>();
    @OneToMany(mappedBy = "owner")
    private Set<Campaign> campaigns = new HashSet<>();
}

更新 2:

完整的堆栈跟踪:

Could not write JSON: failed to lazily initialize a collection of role: .entities.user.User.machineGroups, could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: .entities.user.User.machineGroups, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->.entities.machinegrouptoversion.MachineGroupToVersionDTO[\"machineGroup\"]->.entities.machinegroup.MachineGroup[\"users\"]->org.hibernate.collection.internal.PersistentSet[0]->.entities.user.User[\"machineGroups\"])

更新 3:

MachineGroupToVersionService

 @Transactional(transactionManager = "primaryTransactionManager", propagation= Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class)
    public List<MachineGroupToVersionDTO> getByMachineGroupName(String mgName) {
        List<MachineGroupToVersionDTO> mgtvl = new ArrayList<>();
        Optional<MachineGroup> mg = machineGroupService.getByName(mgName);
        if(mg.isPresent())
            mgtvl = machineGroupToVersionRepository.findByMachineGroup(mg.get()).stream()
                    .map(this::convertToDto).collect(Collectors.toList());
        return mgtvl;
    }

    private MachineGroupToVersionDTO convertToDto(MachineGroupToVersion mgtv) {
        MachineGroupToVersionDTO machineGroupToVersionDTO = new MachineGroupToVersionDTO();
        machineGroupToVersionDTO.setMachineGroup(mgtv.getMachineGroup());
        machineGroupToVersionDTO.setVersion(mgtv.getVersion());
        machineGroupToVersionDTO.setCreationTime(mgtv.getCreationTime());
        machineGroupToVersionDTO.setState(mgtv.getState());
        machineGroupToVersionDTO.setTestedTime(mgtv.getTestedTime());
        return machineGroupToVersionDTO;

    }

【问题讨论】:

  • 错误与user 实体中的role 实体有关。也贴User的实体类。
  • “角色”关键字是错误本身的一部分,问题出在 MachineGroup 实体中设置的用户范围内
  • 请发User实体类。
  • role 不是错误的关键字部分。必须是User实体中的字段名。
  • 如果你用谷歌搜索这个错误,你会看到“无法延迟初始化角色集合:等等等等”,但我还是添加了 User 类。

标签: java hibernate relationship


【解决方案1】:

您可能正在尝试在不打开休眠事务会话的情况下获取惰性属性(通过休眠代理实例化惰性集合所必需的)。将org.springframework.transaction.annotation.Transactional(readOnly = true) 注释添加到您用来获取MachineGroup 集合的方法中。

编辑: 您可能面临循环获取问题,在服务方法上按名称获取 MachineGroup 时,它会获取 MachineGroup 以及所有用户通过急切模式 (@ManyToMany(fetch = FetchType.EAGER)),它们也有一个 MachineGroup 列表。

在你的 DTO 转换方法中,你设置了一个MachineGroup,它有一个用户列表,要设置这个用户列表,每个用户必须再次拥有一个MachineGroup 列表,默认情况下会延迟获取,这就是抛出异常的确切断点:

Could not write JSON: failed to lazily initialize a collection of role: .entities.user.User.machineGroups, could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: .entities.user.User.machineGroups, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->.entities.machinegrouptoversion.MachineGroupToVersionDTO[\"machineGroup\"]->.entities.machinegroup.MachineGroup[\"users\"]->org.hibernate.collection.internal.PersistentSet[0]->.entities.user.User[\"machineGroups\"])

如果您需要检索多对多表machine_groups_to_users 的信息,我建议您使用composite primary key 创建一个实体,并从您的用户实体中删除MachineGroup 的列表。

【讨论】:

  • 我正在通过其 Repository MachineGroupToVersionRepository 获取 MachineGroupToVersion 实体,正如您所见 MachineGroupToVersion 引用了 MachineGroup,我没有显式调用 MachineGroup 服务中的方法,这一切都是通过 MachineGroupToVersion 完成的。如果您可以检查,我也将服务添加到问题中
【解决方案2】:

问题在于,在您的MachineGroupToVersionDTO machineGroup 设置中,MachineGroup#users 集合至少包含一个User,它引用了一些MachineGroupUser#machineGroups,它没有被初始化。您可以尝试加入 fetch 所有这些,也可以在周围散布一些 @JsonIgnore 注释,但我怀疑这对您有用,因为您可能希望为其他一些端点序列化用户的机器组。所以最后,你唯一的选择(在我看来)是你一直引入 DTO,并且不要在你的 DTO 模型中使用任何实体。

我认为这是Blaze-Persistence Entity Views 的完美用例。

我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:

@EntityView(MachineGroupToVersion.class)
public interface MachineGroupToVersionDto {
    @IdMapping
    Long getId();
    String getState();
    Date getCreationTime();
    Date getTestedTime();
    VersionDto getVersion();
    MachineGroupDto getMachineGroup();

    @EntityView(Version.class)
    interface VersionDto {
        @IdMapping
        Long getId();
        String getName();
    }
    @EntityView(MachineGroup.class)
    interface MachineGroupDto {
        @IdMapping
        Long getId();
        String getName();
        Date getCreationTime();
        Set<UserDto> getUsers();
    }
    @EntityView(User.class)
    interface UserDto {
        @IdMapping
        Long getId();
        String getUsername();
    }
}

查询是将实体视图应用于查询的问题,最简单的就是通过 id 进行查询。

MachineGroupToVersionDto a = entityViewManager.find(entityManager, MachineGroupToVersionDto.class, id);

Spring Data 集成让您可以像使用 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<MachineGroupToVersionDto> findAll(Pageable pageable);

最好的部分是,它只会获取实际需要的状态!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多