简体   繁体   中英

JPA Lazy loading is not working in Spring boot

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.

在此处输入图像描述

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

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

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.

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

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.

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

检索到的数据已经延迟,但您正在使用调试模式,当单击从调试器查看数据时,它的返回值。

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

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.

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

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM