4

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:

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:

[
{
    "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": "[email protected]",
        "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": "[email protected]",
        "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.

What am I missing here?

13
  • 6
    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? Commented Jun 30, 2018 at 7:27
  • @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 Commented Jun 30, 2018 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. Commented Jun 30, 2018 at 9:28
  • 1
    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? Commented Jun 30, 2018 at 9:34
  • 1
    I shouldn't be getting User object what does that mean? Which code did you execute, what did you expect it to do, and what did it do instead? You will always have a User inside a suggestion, unless the suggestion is not linked to any user. But the state of the User will be loaded lazily. That's what matters: avoid useless SQL queries. If you never call any method on the user of the suggestion, no query will be executed to load the user data from the database. Commented Jun 30, 2018 at 9:46

2 Answers 2

6

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

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

2 Comments

Thanks for your response. But, I still get the user object in the JSON.
@JsonBackReference works well to hide lazy data in json
5

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.