21

I googled a lot and It is really bizarre that Spring Boot (latest version) may not have the lazy loading is not working. Below are pieces of my code:

My resource:

 public ResponseEntity<Page<AirWaybill>> searchAirWaybill(CriteraDto criteriaDto, @PageableDefault(size = 10) Pageable pageable{
airWaybillService.searchAirWaybill(criteriaDto, pageable);
        return ResponseEntity.ok().body(result);
}

My service:

@Service
@Transactional
public class AirWaybillService {

//Methods

 public Page<AirWaybill> searchAirWaybill(AirWaybillCriteriaDto searchCriteria, Pageable pageable){
    //Construct the specification
            return airWaybillRepository.findAll(spec, pageable);
   }
}

My Entity:

@Entity
@Table(name = "TRACKING_AIR_WAYBILL")
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@airWaybillId") //to fix Infinite recursion with LoadedAirWaybill class
public class AirWaybill{
//Some attributes
    @NotNull
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "FK_TRACKING_CORPORATE_BRANCH_ID")
    private CorporateBranch corporateBranch;
}

And when debugging, I still getting all lazy loaded attributed loaded. See image below.

enter image description here

One of my questions is could Jackson be involved in such behaviour? Is there any way that I may have missed to activate the lazy loading?

EDIT

Another question, could the debugger be involved in ruining the lazy loading?

EDIT 2:

For specification build, I have :

public static Specification<AirWaybill> isBranchAirWayBill(long id){
    return new Specification<AirWaybill>() {
        @Override
        public Predicate toPredicate(Root<AirWaybill> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            return cb.equal(root.join("corporateBranch",JoinType.LEFT).get("id"),id);
        }
    };
}
5
  • 1
    where is transactional configured? on the resource or the service? Commented Apr 16, 2019 at 7:30
  • 1
    On the service. I updated the question Commented Apr 16, 2019 at 7:32
  • Does your criteria have any conditions on corporateBranch? If so, how do you build the spec passed to repository? Commented Apr 16, 2019 at 8:04
  • Yes, it does but conditionally and even I removed the specification and loaded only a page but I still see the problem Commented Apr 16, 2019 at 8:17
  • 1
    Just to discard the issue is debugger-related, enable the Spring Boot SQL query logging: stackoverflow.com/questions/30118683/… Commented Apr 16, 2019 at 9:27

11 Answers 11

24

Hibernate Session exists within method with @Transactional. Passing entity outside Service class is a bad practise because session is being closed after leaving your search method. On the other hand your entity contains lazy initialised collections, which cannot be pulled once session is closed.

The good practise is to map entity onto transport object and return those transport objects from service (not raw entities).

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

3 Comments

precise concise and understandable
I am sorry, but I don't understand it. Do you have an example somewhere showing how this transport design is setup? Thank you in advance
Please create simple POJO (let's say AirWaybillTO and put all attributes you need there, then map them from AirWaybill and return AirWaybillTO from your service.
9

SpringBoot by default has enabled:
spring.jpa.open-in-view = true
That means transaction is always open. Try to disable it.
more information here

2 Comments

Nice help, I'm now getting org.hibernate.LazyInitializationException: could not initialize proxy - no Session. I tried installing Hibernate5Module but did not work yet, still same error
You can fix this with @Transactional. See Kowalski's answer.
6

Most likely you are debugging while still being inside the service, thus while the transaction is still active and lazy loading can be triggered (any method called on a lazy element triggered the fetch from the database).

The problem is that lazy loading cannot occur while being outside of the transaction. And Jackson is parsing your entity definitely outside the boundaries of one.

You either should fetch all the required dependencies when building your specification or try with the @Transactional on the resource level (but try that as of last resort).

Just so that you know, LAZY fetching strategy is only a hint.. not a mandatory action. Eager is mandatory:

The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified.

2 Comments

Even when I see the response in my browser I see the all lazy loaded attributes are loaded (which I don't like to). So the debugger has nothing to do with the loaded children
chech my upated.
4

I suppose you are using Hibernate as JPA.

From specification:

The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified. https://docs.jboss.org/hibernate/jpa/2.2/api/javax/persistence/FetchType.html

Hibernate ignores fetch type specially in OneToOne and ManyToOne relationships from non owning side.

There are few options how to force Hibernate use fetch type LAZY if you really need it.

  1. The simplest one is to fake one-to-many relationship. This will work because lazy loading of collection is much easier then lazy loading of single nullable property but generally this solution is very inconvenient if you use complex JPQL/HQL queries.
  2. The other one is to use build time bytecode instrumentation. For more details please read Hibernate documentation: 19.1.7. Using lazy property fetching. Remember that in this case you have to add @LazyToOne(LazyToOneOption.NO_PROXY) annotation to one-to-one relationship to make it lazy. Setting fetch to LAZY is not enough.
  3. The last solution is to use runtime bytecode instrumentation but it will work only for those who use Hibernate as JPA provider in full-blown JEE environment (in such case setting "hibernate.ejb.use_class_enhancer" to true should do the trick: Entity Manager Configuration) or use Hibernate with Spring configured to do runtime weaving (this might be hard to achieve on some older application servers). In this case @LazyToOne(LazyToOneOption.NO_PROXY) annotation is also required.

For more informations look at this: http://justonjava.blogspot.com/2010/09/lazy-one-to-one-and-one-to-many.html

1 Comment

Same thing is mentioned in this blog written by Vlad Mihalcea about @OneToOne mapping and LAZY loading: vladmihalcea.com/…
3

When using a debugger, you are trying to access the value of your variables. So, at the moment you click that little arrow on your screen, the value of the variable in question is (lazily) loaded.

2 Comments

Thanks. But even before I click the arrow I see in the line of that attribute that already has the information
IDEs handle debugging differently. InteliJ loads everything in advance for you, but another IDE may not.
2

Another consideration is while using Lombok, @Data/@Getter annotation causes to load lazy items without need. So be careful when using Lombok.

This was my case.

Comments

1

Just a guess: you are forcing a fetch while building your specification.

I expect something like

static Specification<AirWaybill> buildSpec() {
    return (root, query, criteriaBuilder) -> {
       Join<AirWaybill, CorporateBranch> br = (Join) root.fetch("corporateBranch");
       return criteriaBuilder.equal(br.get("addressType"), 1);
    };
}

If this is the case, try changing root.fetch to root.join

1 Comment

Thanks for your notice, please see the updated above
0

You can solve this problem with wit 2 steps with jackson-datatype-hibernate:

kotlin example

  1. Add In build.gradle.kts:
implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
  1. Create @Bean
   @Bean
   fun hibernate5Module(): Module = Hibernate5Module()

Notice that Module is com.fasterxml.jackson.databind.Module, not java.util.Module

Comments

0

I think I might have a solution. You can give this a try. This worked for me after 4 hours of hit and trial -

User Entity :

class User {
    @Id
    String id;

    @JsonManagedReference
    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    private List<Address> addressDetailVOList = new ArrayList<Address>();
} 

Address entity :

class Address {

    @JsonBackReference
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "userId")
    private User user;
}

Your parent class will use @JsonManagedReference, and child class will use @JsonBackReference. With this, you can avoid the infinite loop of entity objects as response and stack overflow error.

Comments

-1

The retrieved data already lazy but you are using debug mode its return value when click to watch a data from a debugger.

1 Comment

add such an answer in comments.
-1

I also faced the same issue with Spring data JPA. I added the below annotation & able to get the customer records for a given ORDER ID

Customer to Order : one to Many

Order to customer is lazy load.

Order.java

@ManyToOne(cascade = CascadeType.ALL,targetEntity = CustomerEntity.class,fetch = FetchType.LAZY)
@Fetch(FetchMode. JOIN)
@JoinColumn(name = "CUSTOMER_ID",referencedColumnName = "CUSTOMER_ID",insertable = false,updatable = false)
@LazyToOne(LazyToOneOption.PROXY)
Private CustomerEntity customer

Customer.java

@Entity
@TabLe(name = "CUSTOMER" ,
uniqueConstraints = @UniqueConstraint(columnNames= {"mobile"}))
public class CustomerEntity {

@GeneratedVaLue(strategy = GenerationType.IDENTITY)
@CoLumn(name = "customer_id" )
private Integer customerld;
private String name;
private String address;
private String city;
private String state;
private Integer zipCode;
private Integer mobileNumber;

@OneToMany(mappedBy = " customer" )
@Fetch(FetchMode.JOIN)
@LazyToOne(LazyToOneOption.PROXY)
private List<OrderEntity> orders;
}

2 Comments

Please add code and data as text (using code formatting), not images. Images: A) don't allow us to copy-&-paste the code/errors/data for testing; B) don't permit searching based on the code/error/data contents; and many more reasons. Images should only be used, in addition to text in code format, if having the image adds something significant that is not conveyed by just the text code/error/data.
can someone explain this annotation. in Documentation, it is not properly explained. baeldung.com/hibernate-lazy-loading-workaround after reading this article, it seems @ Transactional is better, but if we add "enable_lazy_load_no_trans" then it will call for list of object & each object each refered lazy object in one trasnaction per object. Is @LazyToOne also will do one time one transaction or is it one extra txn in total only?

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.