在将对象转换为json期间,无法懒惰地初始化角色集合
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在将对象转换为json期间,无法懒惰地初始化角色集合相关的知识,希望对你有一定的参考价值。
我是Spring的新手,在从与其他表有关系的表中获取记录时会出现这种懒惰的初始化错误。我在网上看了很多但没有得到合适的方法。
表格1:
@SuppressWarnings("serial")
@Entity
public class Terminal extends BaseEntity {
@Column(length = 100, unique = true)
private String shortName;
@Column
private short number; // short stores up to 32767 value
@Column
private String description;
@OneToMany
@JoinColumn(name = "terminal_id", referencedColumnName = "uuid")
@Cascade({ CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DELETE })
private Set<BusinessHour> businessHour;
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
public short getNumber() {
return number;
}
public void setNumber(short number) {
this.number = number;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Set<BusinessHour> getBusinessHour() {
return businessHour;
}
public void setBusinessHour(Set<BusinessHour> businessHour) {
this.businessHour = businessHour;
}
表2:
@SuppressWarnings("serial")
@Entity
public class BusinessHour extends BaseEntity {
@Column
private DayOfWeek dayOfWeek;
@Column
private LocalTime startOfOperation;
@Column
private LocalTime endOfOperation;
public DayOfWeek getDayOfWeek() {
return dayOfWeek;
}
}
服务代码:
@Service
public class TerminalServiceImpl implements TerminalService {
@Autowired
TerminalRepository terminalRepository;
Iterable<Terminal> allTerminals = terminalRepository.findAll();
List<Terminal> terminalList = new ArrayList<Terminal>();
for (Terminal terminal : allTerminals) {
terminalList.add(terminal);
}
return terminalList;
}
终端存储库代码:
@Transactional
public interface TerminalRepository extends CrudRepository<Terminal, Long> {
}
我在调试期间遇到错误的代码:
private List<Terminal> updateTerminalList() {
List<Terminal> allTerminals = terminalService.fetchAllTerminal();
return allTerminals;
}
public void terminalWrapperRun() {
try {
Payload payload = createTerminalPayload(applicationId);
String json3 = object2Json(payload);
kafkaRESTUtils.sendServerPayload(json3);
} catch (Exception e1) {
e1.printStackTrace();
}
}
public String object2Json(Object dataArray) throws JsonProcessingException {
return mapper.writeValueAsString(dataArray);
}
错误:
com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: terminal.model.Terminal.businessHour, could not initialize proxy - no Session (through reference chain:
将获取对象转换为json时获取异常。我发现由于代理对象返回由于提取类型懒惰(我希望保持原样)。
我相信这个问题与你的ORM默认的LAZY加载集相关。
@OneToMany
@JoinColumn(name = "terminal_id", referencedColumnName = "uuid")
@Cascade({ CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DELETE })
private Set<BusinessHour> businessHour;
@OneToMany注释具有fetch属性,默认情况下设置为LAZY。
FetchType fetch() default LAZY;
这意味着只有在访问数据时才会加载它。对于您的示例,当您尝试创建JSON字符串时会发生这种情况。但是,到目前为止,您不在ORM会话的范围内,因此它不知道如何加载数据。
因此,您有2个选项。
- 更改注释以急切加载数据(这意味着BusinessHour Set将与父对象同时加载 @OneToMany(fetch = FetchType.EAGER)
- 在ORM会话中执行您的JSON生成(我只是建议这样做是第一个导致性能问题的选项)。
如果我没记错的话,这是由于Entity
在使用时与EntityManager
分离引起的那种错误(因为它是Proxy
,它无法执行数据库查询来检索数据)。
您可以使用:
@OneToMany(fetch = FetchType.EAGER)
...
private Set<BusinessHour> businessHour;
使用FetchType = EAGER意味着针对您的实体的任何查询都将加载整组带注释的实体。
Imho,如果您100%确定您的实体仅用于您的特殊商业案例,这只是一个明智的行动。
在所有其他情况下 - 比如将数据编程为库,或者在实体上接受不同类型的查询,您应该使用实体图(https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs002.htm)或显式加载(Hibernate.initialize,join-fetch,请参阅https://vladmihalcea.com/hibernate-facts-the-importance-of-fetch-strategy/)。
如果您的用例只是转换,那么您有两个不错的选择:
- 在Transactional方法中将您的实体转换为JSON(如PillHead建议的那样)
- 在事务中使用所需的所有实体(通过实体图或Hibernate.initialize)显式加载您的实体,然后在需要的地方转换为JSON。
以上是关于在将对象转换为json期间,无法懒惰地初始化角色集合的主要内容,如果未能解决你的问题,请参考以下文章
无法懒惰地初始化角色集合,..无法初始化代理 - 无会话 - JPA + SPRING
Spring Boot 中的 Hibernate 无法懒惰地初始化角色集合,无法初始化代理 - 没有 Session 异常