2

I'm using spring boot with JPA ( hibernate ) , Before I move to spring boot I was using Spring data JPA with hibernate and the default is to load property values eagerly and to load collections lazily.

in spring boot JPA the following is fetched eagerly by default why ? The roles will be returned inside the user but they should be null

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles;

That's the example I followed https://medium.com/@gustavo.ponce.ch/spring-boot-spring-mvc-spring-security-mysql-a5d8545d837d

And the application.properties I have edited to the follow :

# ===============================
# = JPA / HIBERNATE
# ===============================
spring.jpa.show-sql = true
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

# ==============================================================
# = Spring Security / Queries for AuthenticationManagerBuilder
# ==============================================================
spring.queries.users-query=select email, password, active from user where email=?
spring.queries.roles-query=select u.email, r.role from user u inner join user_role ur on(u.id=ur.user_id) inner join role r on(ur.role_id=r.id) where u.email=?

=========================================================================== I have tried to add this relation in the user Entity :

@Entity
@Table(name = "user")
@Getter
@Setter
public class User {

    @OneToMany(mappedBy = "user")
    private List<Test> tests;
}

the tests will be fetched eagerly in this :

@Transactional
@Override
public List<User> getAllUsers() {
        List<User>users=userRepository.findAll();
    return users;
}

I can't find why this is default instead of lazy loading.

Mohammad Karmi
  • 1,403
  • 3
  • 26
  • 42
  • Spring Boot doesn't change anything nor does Spring Data JPA. The collection shouldn't be null it is a proxy (which will lazily retrieve the collection). Spring Boot does however by default enable the the open entitymanager in view pattern which opens the `EntityManager` at the start of a http request and closes it at the end. So I guess you are seeing the behavior of that. So nothing will be changed to eager, it still are the defaults. Also if you inspect the list inside the `getAllUsers` with a debugger you still will see them as the entitymanager is bound to the transaction. – M. Deinum Sep 18 '18 at 08:26
  • @M.Deinum Thanks for reply. But in Spring Data JPA I can see in the debugger these values are null . But I'm confused if this is a proxy and not fetched eagerly and I get the same result as eager fetch what is the difference ? – Mohammad Karmi Sep 18 '18 at 08:36
  • See my comment... The open session in view and probably the fact that you now have a correct transaction where you before didn't have that. – M. Deinum Sep 18 '18 at 08:40
  • @M.Deinum can you please post the answer below ? I will accept it – Mohammad Karmi Sep 18 '18 at 08:42
  • @M.Deinum please note I was getting this before spring boot : org.hibernate.LazyInitializationException failed to lazily initialize a collection , so if you can mention please why I need eager fetch if lazy load will fetch them as need and it will work. what is the case it wont work ? – Mohammad Karmi Sep 18 '18 at 08:53
  • As stated read the comment...Either proper transactions now where you earlier didn't have them, or the fact that open entitymanager in view pattern is by default applied where you earlier didn't. – M. Deinum Sep 18 '18 at 09:25
  • @M.Deinum you are totally right . I found that all the http request goes inside one entity manager. how can I modify it to 1 transaction per entity manager ? – Mohammad Karmi Sep 18 '18 at 10:02

3 Answers3

8

You are mistaken. Every mapping that ends with toMany is by default fetched lazily.
Copied from the Spring source files:

@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface ManyToMany {

    // rest of the annotation properties are omitted for better readability

    FetchType fetch() default LAZY;
}
senjin.hajrulahovic
  • 2,961
  • 2
  • 17
  • 32
  • Thats what I meant by collection. When you have tomany you should have a collection. But why in spring boot i get ManyToMany loaded eagrly? – Mohammad Karmi Sep 17 '18 at 20:38
  • Spring Boot just build on top of Spring to ease configuring and running your application. Your problem is most likely not Spring Boot related. – senjin.hajrulahovic Sep 17 '18 at 20:52
4

Spring Boot nor Spring Data JPA changes the default behavior of resolving collections lazily. You probably have one of two the situations when using Spring Boot

  1. Previously your @Transactional wasn't properly applied due to wrong configuration
  2. Spring Boot by default enables the OpenEntityManagerInViewInterceptor for controllers.

Spring Boot manages transactions for you and will have the processing of @Transactional turned on by default. So when inspecting the result inside the @Transactional method using a debugger the result will still be retrieved due to an ongoing transaction.

If that isn't the case you are seeing the effects of the OpenEntityManagerInViewInterceptor. Which leads to opening an EntityManager at the start of the request and close it after view rendering.

To disable this behavior (and assuming you are using a recent Spring Boot version) you can disable it by setting the spring.jpa.open-in-view property to false.

spring.jpa.open-in-view=false
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
0

As I know and according to this answer, cascade and fetch different concerns.

By default @ManyToManyis fetchs lazy as follow :

@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface ManyToMany {

    ...

    /** (Optional) Whether the association should be lazily loaded or
     * must be eagerly fetched. The EAGER strategy is a requirement on
     * the persistence provider runtime that the associated entities
     * must be eagerly fetched.  The LAZY strategy is a hint to the
     * persistence provider runtime.
     */
    FetchType fetch() default LAZY;

    ....
}

I just want to ask for clarify, do you use lombok on your entity?

Emre Savcı
  • 3,034
  • 2
  • 16
  • 25