繁体   English   中英

SPRING JPA 延迟加载数据以在其他类中使用

[英]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()方法是事务性的。 您可以:

  • 像这样使用 JPQL:

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM