Spring Boot JPA Lazy Fetch is not working


Spring Boot JPA Lazy Fetch is not working



I have following two domain objects Suggestion and UserProfile



They are mapped with each other in one to many relationship. When I fetch all suggestions using Spring Data JPA, I get corresponding user object with each suggestion objects. This result is observed even when I have set fetch as FetchType.Lazy. Following is my code:


fetch


FetchType.Lazy



Suggestion.java


@Entity
@Table(name="suggestion")
@JsonIgnoreProperties({"suggestionLikes"})
public class Suggestion {

public Suggestion() {
// TODO Auto-generated constructor stub
}

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

@Column(name="description")
private String description;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="suggestion_by")
private UserProfile user;

//getters and setters
}



UserProfile.java


@Entity
@Table(name = "user_master")
@JsonIgnoreProperties({"suggestions", "suggestionLikes"})
public class UserProfile implements Serializable {

/**
*
*/
private static final long serialVersionUID = 7400472171878370L;


public UserProfile() {

}


@Id
@NotNull
@Column(name = "username", length = 55)
private String userName;

@NotNull
@Column(name = "password")
private String password;

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

//getters and setters
}



Following is the service which fetches the records:


@Override
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public List<Suggestion> getAllSuggestion() {
return suggestionRespository.findAll();;
}



SuggestionRepository:


@Repository
public interface SuggestionRespository extends JpaRepository<Suggestion,
Integer> {

public List<Suggestion> findAll();
}



Following is the Application class:


@EnableTransactionManagement
@SpringBootApplication
public class AngularSpringbootApplication {

public static void main(String args) {
SpringApplication.run(AngularSpringbootApplication.class, args);
}
}



application.properties:


spring.datasource.url=jdbc:mysql://localhost:3306/plan_trip
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.properties.hibernate.dialect =
org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update
spring.jackson.serialization.fail-on-empty-beans=false



Response received when getAllSuggestions() is executed:


getAllSuggestions()


[
{
"suggestionId": 2,
"description": "Germanyi!",
"createdBy": "vinit2",
"createdDate": "2018-06-19T10:38:32.000+0000",
"modifiedBy": "vinit2",
"modifiedDate": "2018-06-19T10:38:32.000+0000",
"user": {
"userName": "vinit2",
"password":
"$2a$10$.hP0sQWpl6qqDKiNTkiu0OciQeHRFnkEbEWcDvnv1HY4QCi2tKo.2",
"firstName": "Vinit2",
"lastName": "Divekar2",
"emailAddress": "vinit@gmail.com",
"createdBy": null,
"modifedBy": null,
"createdDate": "2018-06-04",
"modifiedDate": "2018-06-04",
"isActive": "1",
"handler": {},
"hibernateLazyInitializer": {}
}
},
{
"suggestionId": 1,
"description": "Vasai!",
"createdBy": "vinit1",
"createdDate": "2018-06-19T10:37:38.000+0000",
"modifiedBy": "vinit1",
"modifiedDate": "2018-06-19T10:37:38.000+0000",
"user": {
"userName": "vinit1",
"password": "$2a$10$D0RMSTWu03Jw7wC1/zqFxOOjb0Do24o/4mq2PhDhRUyBrs8bdGvUG",
"firstName": "Vinit1",
"lastName": "Divekar1",
"emailAddress": "vinit@gmail.com",
"createdBy": null,
"modifedBy": null,
"createdDate": "2018-06-04",
"modifiedDate": "2018-06-04",
"isActive": "1",
"handler": {},
"hibernateLazyInitializer": {}
}
}



]



Expected response:


[{
"suggestionId": 2,
"description": "Germanyi!",
"createdBy": "vinit2",
"createdDate": "2018-06-19T10:38:32.000+0000",
"modifiedBy": "vinit2",
"modifiedDate": "2018-06-19T10:38:32.000+0000"
},
{
"suggestionId": 1,
"description": "Vasai!",
"createdBy": "vinit1",
"createdDate": "2018-06-19T10:37:38.000+0000",
"modifiedBy": "vinit1",
"modifiedDate": "2018-06-19T10:37:38.000+0000"
}
]



When I have declared FetchType as Lazy, I should not get User objects (in JSON) when I execute findAll()on Suggestion entity.


FetchType


Lazy


findAll()



What am I missing here?





You're missing what lazy means. lazy doesn't mean "store null instead of the user in the loaded suggestions". It means "only load the actual user data from the database when the code tries to access this data for the first time", for example using suggestion.getUser().getUsername(). Also, don't cast the List to an ArrayList. There is absolutely no guarantee that the returned list is an ArrayList, and your code shouldn't care. Why are you doing that?
– JB Nizet
Jun 30 at 7:27



suggestion.getUser().getUsername()





@JBNizet, thanks for your response. What I understand from 'Lazy' fetch means is; 'Don't give me children until I ask them for when I access their parent'. Is that correct? I referred this question. Also, thank you for the suggestion, I will update my question accordingly. I have updated
– Vinit Divekar
Jun 30 at 9:26






No. It means that the state of the child will only be loaded from the database when you try to access this state for the first time, i.e. call a method on the child object for the first time.
– JB Nizet
Jun 30 at 9:28





Ohk, Sure. In the code given, I don't call the getUser() method for all suggestions. I just call getAllSuggestion().
– Vinit Divekar
Jun 30 at 9:33


getUser()


getAllSuggestion()





Again, calling getUser() is not what will load the state of the user from the database. Calling a method on the User returned by getUser() will do that. Why do you think lazy loading is not working? How did you make this conclusion?
– JB Nizet
Jun 30 at 9:34





2 Answers
2



when you declare fetch = FetchType.LAZY, it means hibernate create a proxy for this field in runtime. when getter of this field is called. the hibernate executes another select to fetch this object.
in case of your problem getter of "user" field is called by Jackson (if you use rest).
so if you don't want "user", try using a model mapper framework (dozer mapper is a good framework).



You can use @JsonManagedReference & @JsonBackReference to prevent proxy call by jakson. Following code may help you.


@JsonBackReference
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<Suggestion> suggestions;

@JsonManagedReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "suggestion_by")
private UserProfile user;



Add the following dependency, change version according to your hibernate


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



and finally add a new config


@Configuration
public class JacksonConfig {

@Bean
public Jackson2ObjectMapperBuilderCustomizer addCustomBigDecimalDeserialization() {
return new Jackson2ObjectMapperBuilderCustomizer() {
@Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
jacksonObjectMapperBuilder.modules(new Hibernate5Module());
}

};
}
}





Thanks for your response. But, I still get the user object in the JSON.
– Vinit Divekar
15 hours ago







By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Extract Id from Twitch Clip URL

Why are these constructs (using ++) undefined behavior in C?

I'm Still Waiting (Diana Ross song)