Spring Boot JPA-延迟加载不适用于一对一映射

时间:2019-07-23 17:26:39

标签: java hibernate spring-boot jpa jackson

请注意,我已经看过类似的问题,并且已经解释了为什么它们没有为我工作

我有一个简单的Spring boot JPA-Hibernate应用程序,其中User和Address之间是一对一的映射。 (请注意,我没有一对多映射的问题)

用户实体

@Entity
    @Table(name = "users")
    public class User implements Serializable {

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
        private Integer id;

        @Column
        private String name;

        @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
        private Address address;

        @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
        private Set<Note> notes;

    }

地址实体

   @Entity
    @Table(name = "addresses")
    public class Address implements Serializable {

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
        private Integer id;

        @Column
        private String street;

        @Column
        private String city;

        @JsonIgnore
        @OneToOne
        @JoinColumn(name = "user_id")
        private User user;

    }

注释实体

@Entity
    @Table(name = "notes")
    public class Note implements Serializable {

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
        private Integer id;

        @Column
        private String date;

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "user_id", nullable = false)
        private User user;

    }

我的问题是,每当我调用映射为要获取所有用户的控制器时,我都会得到该地址以及所有相关的注释。但我希望FetchType.LAZY能够解决这个问题。

我在StackOverflow上阅读了很多问题,提到杰克逊可能是罪魁祸首:

Post 1

我还读到spring.jpa.open-in-view的默认值可能是罪魁祸首:

Post 2

Post 3

所以我尝试了以下选项:

我通过向我的spring.jpa.open-in-view=false中添加application.properties来禁用默认打开的视图属性

Could not write JSON: failed to lazily initialize a collection of role error

我认为这是因为杰克逊正在对我的惰性加载对象调用吸气剂,所以我遵循了另一篇文章中的说明,并为杰克逊添加了以下内容,以使惰性加载的集合不予理会:

pom.xml

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>2.9.9</version>
</dependency>

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (HttpMessageConverter converter : converters) {
            if (converter instanceof org.springframework.http.converter.json.MappingJackson2HttpMessageConverter) {
                ObjectMapper mapper = ((MappingJackson2HttpMessageConverter) converter).getObjectMapper();
                mapper.registerModule(new Hibernate5Module());
            }
        }
    }

}

以上解决方案通过“一对多”映射解决了该问题,但响应中仍关联了“地址”。

我不确定在这里可以做什么。默认登录页面上的用户实体不需要任何地址详细信息,因此我不想将其加载到登录页面上。单击记录后,它将导航到另一个页面,在这里我希望在响应中返回所有延迟加载的对象。

我已经尝试了所有可以在网上找到的内容,但到目前为止仍然没有任何效果。我真的很感谢您的帮助。

一个用户提到它可能是SO另一个问题的重复: Suggested Possible duplicate 我想提到的是,我通过禁用spring.jpa.open-in-view属性但添加了

mapper.registerModule(new Hibernate5Module());

在响应中带回与用户关联的地址。

3 个答案:

答案 0 :(得分:1)

您可以看看Jackson Serialization Views

我已经研究了您尝试过的Hibernate5模块,它具有一些有趣的功能...但是没有人可以为您解决这个问题。

顺便说一句,我通常通过不返回实体作为响应而是返回DTO来解决此问题。

答案 1 :(得分:1)

它的工作方式与JPA规范相同:-

请参考以下网址 https://javaee.github.io/javaee-spec/javadocs/javax/persistence/FetchType.html

LAZY提取策略只是一个提示(如javadoc所述,可以延迟获取数据)..不是强制性操作。

急切是强制性的(因为javadoc说必须急切地获取数据)。

答案 2 :(得分:1)

问题是杰克逊在编写JSON时会触发初始化,因此请不要编写当前字段(地址)。但是您不应该使用@jsonIgnore,因此在其他地方您可以返回Eager obj。

您可以使用@jsonView注释,该注释可以在不同请求下为同一obj提供不同的JSON。您可以看一下以下示例:

创建视图类:

public class ViewFetchType {
    static class lazy{ }
    static class Eager extends lazy{ }
}

注释您的实体

@Entity
public class User {

    @Id
    @JsonView(ViewFetchType.Lazy.class)
    private String id;

    @JsonView(ViewFetchType.Eager.class)
    @OneToOne( fetch = FetchType.LAZY)
    private Address address ;
}

在您的控制器中指定FetchType类:

public class UserController {

    private final UserRepository userRepository;

    @Autowired
    UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @RequestMapping("get-user-details")
    @JsonView(ViewFetchType.Eager.class)
    public @ResponseBody Optional<User> get(@PathVariable String email) {
        return userRepository.findByEmail(email);
    {

    @RequestMapping("get-all-users")
    @JsonView(ViewFetchType.Lazy.class)
    public @ResponseBody List<User> getUsers() {
        return userRepository.findAll();
    }
}

这是我从... https://stackoverflow.com/a/49207551/10162200

中获得灵感的答案
相关问题