Hibernate -惰性初始化失败



我试图从数据库中加载一个实体与hibernate,但我得到以下错误

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 . eagle ">

?更新:

用户类

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;
}

您可能正在尝试在不打开hibernate事务会话(这是通过hibernate代理实例化惰性集合所必需的)的情况下获取惰性属性。将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的信息,我建议您创建一个具有复合主键的实体,并从您的用户实体中删除MachineGroup的列表。

问题是,在您的MachineGroupToVersionDTOmachineGroup设置中,MachineGroup#users集合包含至少一个User,引用一些MachineGroupUser#machineGroups,未初始化。你可以尝试连接取所有这些,或者洒一些@JsonIgnore注释,但我怀疑这对你来说是否有效,因为你可能想要序列化其他端点的用户的机器组。所以最后,你唯一的选择(在我看来)是你一直引入DTO,并且在你的DTO模型中不使用任何实体。

我认为这是火焰持久实体视图的完美用例。

我创建的库允许JPA模型和自定义接口或抽象类定义模型之间的轻松映射,类似于类固醇上的Spring Data projection。其思想是,您以您喜欢的方式定义您的目标结构(域模型),并通过JPQL表达式将属性(getter)映射到实体模型。

您的用例的DTO模型可以像下面这样使用Blaze-Persistence Entity-Views:

@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 projection一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<MachineGroupToVersionDto> findAll(Pageable pageable);

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