[英]SPRING JPA Lazy loading data to use in other class
我正在为一个项目使用 Spring Boot,但我一直在使用延迟加载。
我想要做的是在我的控制器中加载数据,然后发送到可呈现的对象,这将提取所需的信息,而 JSON 序列化器会做不好的工作来创建我的自定义 HTTP 响应。
当 UserPresentation 类调用文件夹 getter 时会出现问题,错误是众所周知的: could not initialize proxy - no Session
。
当然,文件夹的默认提取是 LAZY,我想要这个,但我不知道如何准备对象以在演示文稿中使用。 我只复制粘贴文件夹设置为清晰和简短,但我在 User 类中有更多集合,所有这些都给我同样的问题。
我知道我可以在控制器中调用 getter 来初始化集合,但我发现这就像一个硬编码,事实上,如果我想添加一些东西来展示我也需要在控制器中做。
我也用@Transactional
尝试过,但没有用。
这是我的课:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "USER_ID")
private Integer id;
@Column(unique = true)
private String email;
private String password;
@Enumerated(EnumType.STRING)
private Authority userAuthority;
@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL)
private Set<Folder> ownFolders = new HashSet<>();
... getter setter
}
@RestController
public class UserController {
@GetMapping(value = "/api/user", produces = APPLICATION_JSON_VALUE)
public CustomResponseEntity userInfo() {
User currentUser = loginService.getCurrentUser();
UserPresentation userPresentation = new UserPresentation(currentUser);
return ResponseManager.respondData(userPresentation);
}
}
public class UserPresentation implements Presentable {
private User user;
public UserPresentation(User user) {
this.user = user;
}
public Integer getId() {
return user.getId();
}
public String getEmail() {
return user.getUsername();
}
public String getAuthority() {
return user.getUserAuthority().name();
}
public boolean isEnabled() {
return user.isEnabled();
}
public Integer getOwnFolders() {
Set<Folder> folderList = user.getOwnFolders();
if (folderList == null)
return 0;
return folderList.size();
}
}
最后两个只是为了清楚
public class ResponseManager {
// DATA
public static ResponseEntity respondData(Presentable presentable, String token) {
CustomResponse response = new DataResponse<>(presentable);
return new ResponseEntity<>(response, HttpStatus.OK);
}
}
public class DataResponse<T extends Presentable> extends CustomResponse {
private T data;
public T getData() {
return data;
}
private void setData(T data) {
this.data = data;
}
public DataResponse(T data) {
this.setData(data);
}
@Override
public String getType() {
return DATA;
}
}
我想你从数据库中加载当前用户:
User currentUser = loginService.getCurrentUser();
getCurrentUser()
方法是事务性的。 您可以:
"select u from User u join fetch u.ownFolders where ... "
加载用户信息(这种方式会急切地获取ownFolders
关系)
或者
getCurrentUser()
() 内调用 user.getOwnFolders() 即可触发提取。我找到了一种方法,即使有点脏,它也可以让我做我想做的事,而无需对代码进行大的改动。
实际上,问题发生在 JSON 序列化期间,它在我的控制之外运行(在发送 HTTP 响应之前的 Spring 类中的某处),所以我在@Transactional
块创建后手动序列化每个Presentable
对象。
这些是更改后的类:
public class UserPresentation implements Presentable {
private User user;
public UserPresentation(User user) {
this.user = user;
this.initialize() //ADDED (called here and in every other class that implements Presentable)
}
...getter and setter (which I want as JSON fields)
}
@RestController
public class UserController {
@Transactional //ADDED
@GetMapping(value = "/api/user", produces = APPLICATION_JSON_VALUE)
public CustomResponseEntity userInfo() {
User currentUser = loginService.getCurrentUser();
UserPresentation userPresentation = new UserPresentation(currentUser);
return ResponseManager.respondData(userPresentation);
}
}
在此修复之前,该接口仅用于在 ResponseManager 中使用 Polymorfism,因此为空
public interface Presentable {
default void initialize() {
try {
new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeJsonMappingException(e.getMessage());
}
}
}
我建议你使用https://github.com/FasterXML/jackson-datatype-hibernate
该模块支持 Hibernate 版本 3.x 、4.x 和 5.x 的数据类型; 以及一些相关的行为,例如延迟加载和瞬态检测(@Transient 注释)。
它知道如何在会话关闭后处理延迟加载,它会在会话外跳过标记为延迟获取的对象的 json 转换
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.9.8</version>
</dependency>
ObjectMapper mapper = new ObjectMapper();
// for Hibernate 4.x:
mapper.registerModule(new Hibernate4Module());
// or, for Hibernate 5.x
mapper.registerModule(new Hibernate5Module());
// or, for Hibernate 3.6
mapper.registerModule(new Hibernate3Module());
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/*
* Here we register the Hibernate4Module into an ObjectMapper, then set this * custom-configured ObjectMapper to the MessageConverter and return it to be * added to the HttpMessageConverters of our application
*/
public MappingJackson2HttpMessageConverter jacksonMessageConverter() {
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper hibernateAwareObjectMapper = new ObjectMapper();
hibernateAwareObjectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
hibernateAwareObjectMapper.enable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// Registering Hibernate5Module to support lazy objects
hibernateAwareObjectMapper.registerModule(new Hibernate5Module());
messageConverter.setObjectMapper(hibernateAwareObjectMapper);
return messageConverter;
}
}
XML 配置
<mvc:annotation-driven>
<mvc:message-converters>
<!-- Use the HibernateAware mapper instead of the default -->
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="path.to.your.HibernateAwareObjectMapper" />
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.