0

Please note that I have looked at similar questions and I have explained why they haven't worked for me

I have a simple Spring boot JPA-Hibernate application with one to one mapping between User and Address. (Please note that I do not have this issue with one to many mapping)

User Entity

@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; } 

Address Entity

 @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; } 

Note Entity

@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; } 

My problem is that whenever I call the controller mapped to get all users I was getting the address and all the associated notes with it as well. But I would expect FetchType.LAZY to take care of that.

I read a lot of questions on StackOverflow mentioning that Jackson might be the culprit here:

Post 1

I also read that spring.jpa.open-in-view defualt value might be the culprit:

Post 2

Post 3

So i tried the following options:

I disabled default open in view property by adding spring.jpa.open-in-view=false to my application.properties which started giving me

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

I am assuming its because Jackson is calling the getters on my lazily loaded objects so I followed the instructions from another post and added the following for Jackson to leave the lazily loaded collections alone:

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

This solution above fixed the issue with the One to Many mapping but still has the Address associated in the response.

I am not sure what can I do here. The User Entity on the default landing page does not need any address details so I do not want to load it on the landing page. When the record is clicked then it navigates to another page and that's where I would like all the lazy loaded objects to be returned in the response.

I have tried everything I could find online but still nothing has worked so far. I would really appreciate some help with this.

As mentioned by one of the users that it might a duplicate of another question on SO: Suggested Possible duplicate I would like to mention that I got the Lazy loading working by disabling spring.jpa.open-in-view property but adding

mapper.registerModule(new Hibernate5Module()); 

brings back the address associated to the User in the response.

16
  • Have you considered serializing some kind of DTO (dedicated per use case rather than single entity)? Commented Jul 23, 2019 at 17:39
  • I have but I would also like to figure out a solution to this issue. I dont want to develop further without exploring all the options. Commented Jul 23, 2019 at 17:59
  • Possible duplicate of Jackson triggering JPA Lazy Fetching on serialization Commented Jul 23, 2019 at 18:54
  • Are you asking (1) how to prevent Jackson from mapping User.address, or (2) how to enable lazy loading on User.adress? Those are very different questions. You already got two answers, each answering a different one out of the two I listed. Note that lazy loading means load when requested, not do not load at all Commented Jul 23, 2019 at 19:09
  • @crizzis I am asking how Jackson can respect lazy loading. Even when I get the lazy loading working, Jackson does not respect it and loads the associations. Commented Jul 23, 2019 at 19:16

3 Answers 3

1

It's working as in the JPA spec:-

Refer the below URL https://javaee.github.io/javaee-spec/javadocs/javax/persistence/FetchType.html

LAZY fetching strategy is only a hint (as the javadoc says the data can be lazily fetched).. not a mandatory action.

Eager is mandatory (as the javadoc says the data must be eagerly fetched).

Sign up to request clarification or add additional context in comments.

1 Comment

If you do not want to retrieve the Address details, consider providing the query in the JPA interface.
1

You may take a look at Jackson Serialization Views.

I´ve taken a look into the Hibernate5 module you tried and it has some interesting features... but none should fix this issue out of the box for you.

By the way, I normally fix this issue by not returning the Entity as the response but DTOs instead.

1 Comment

JsonView might fix the issue that I am having but I am just wondering it would Lazy load calls. Let me try
1

The problem is jackson triggering initialization when he writes the JSON, so just don't write the current field (address). But you should not use @jsonIgnore so at other places you could return an Eager obj.

You can use the @jsonView annotation that can provide different JSON for the same obj at different requests. You can look this example :

Create view class:

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

Annotate your Entity

@Entity public class User { @Id @JsonView(ViewFetchType.Lazy.class) private String id; @JsonView(ViewFetchType.Eager.class) @OneToOne( fetch = FetchType.LAZY) private Address address ; } 

Specify the FetchType class in your controller:

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

Here is the answer that i took the idea from... https://stackoverflow.com/a/49207551/10162200

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.