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

Posted

技术标签:

【中文标题】Spring boot JPA - 延迟加载不适用于一对一映射【英文标题】:Spring boot JPA - Lazy loading is not working for One to One mapping 【发布时间】:2019-12-01 19:22:29 【问题描述】:

请注意,我查看了类似的问题,并解释了为什么它们对我不起作用

我有一个简单的 Spring Boot JPA-Hibernate 应用程序,在用户和地址之间具有一对一的映射。 (请注意,一对多映射没有这个问题)

用户实体

@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 能够解决这个问题。

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

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

我假设它是因为 Jackson 正在调用我延迟加载的对象上的 getter,所以我按照另一篇文章的说明添加了以下内容,让 Jackson 不理会延迟加载的集合:

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());

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

【问题讨论】:

您是否考虑过序列化某种 DTO(专用于每个用例而不是单个实体)? 我有,但我也想找出解决这个问题的办法。如果不探索所有选项,我不想进一步发展。 Jackson triggering JPA Lazy Fetching on serialization的可能重复 您是在问 (1) 如何防止 Jackson 映射 User.address,或者 (2) 如何在 User.adress 上启用延迟加载?这些是非常不同的问题。你已经得到了两个答案,每个答案都是我列出的两个答案中的一个。请注意,延迟加载意味着在请求时加载 根本不加载 @crizzis 我在问杰克逊如何尊重延迟加载。即使我让延迟加载工作,杰克逊也不尊重它并加载关联。 【参考方案1】:

它的工作方式与 JPA 规范中的一样:-

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

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

Eager 是强制性的(正如 javadoc 所说,必须急切地获取数据)。

【讨论】:

如果您不想检索地址详细信息,请考虑在 JPA 界面中提供查询。【参考方案2】:

你可以看看Jackson Serialization Views。

我查看了您尝试过的 Hibernate5 模块,它有一些有趣的功能......但没有一个可以为您解决这个问题。

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

【讨论】:

JsonView 可能会解决我遇到的问题,但我只是想知道它会延迟加载调用。让我试试【参考方案3】:

问题是jackson在写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://***.com/a/49207551/10162200

【讨论】:

以上是关于Spring boot JPA - 延迟加载不适用于一对一映射的主要内容,如果未能解决你的问题,请参考以下文章

JPA 延迟加载

延迟加载有效,但不应该

Spring JPA 延迟加载 - 无法初始化代理

延迟加载 Spring Data JPA 存储库

当加载 spring-boot 和 spring-data-jpa 时,Hibernate 无法加载 JPA 2.1 Converter

从控制台应用程序使用带有休眠功能的spring-data-jpa时如何延迟加载收集