簡體   English   中英

JPA 延遲加載在 Spring 引導中不起作用

[英]JPA Lazy loading is not working in Spring boot

我google了很多,Spring Boot(最新版本)可能沒有延遲加載不起作用,這真的很奇怪。 以下是我的代碼片段:

我的資源:

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

我的服務:

@Service
@Transactional
public class AirWaybillService {

//Methods

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

我的實體:

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

而且在調試時,我仍然會加載所有延遲加載的屬性。 見下圖。

在此處輸入圖像描述

我的問題之一是 Jackson 會參與這種行為嗎? 有什么我可能錯過的激活延遲加載的方法嗎?

編輯

另一個問題,調試器是否會參與破壞延遲加載?

編輯2:

對於規范構建,我有:

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 存在於帶有@Transactional方法中。 在 Service 類之外傳遞實體是一種不好的做法,因為在離開您的search方法后會話將被關閉。 另一方面,您的實體包含延遲初始化的集合,一旦會話關閉就無法拉出。

好的做法是將實體映射到傳輸對象並從服務中返回這些傳輸對象(而不是原始實體)。

很可能您仍在服務內部進行調試,因此當事務仍然處於活動狀態時,可以觸發延遲加載(在延遲元素上調用的任何方法都會觸發從數據庫中獲取)。

問題是在事務之外時不能發生延遲加載。 傑克遜正在解析您的實體,絕對超出了實體的邊界。

您應該在構建規范時獲取所有必需的依賴項,或者在資源級別嘗試使用@Transactional (但作為最后的手段嘗試)。

只是為了讓您知道,LAZY 獲取策略只是一個提示.. 不是強制性操作。 渴望是強制性的:

LAZY 策略是對持久性提供程序運行時的一個提示,即在第一次訪問數據時應該延遲獲取數據。 允許實現急切地獲取已為其指定 LAZY 策略提示的數據。

SpringBoot 默認已啟用:
spring.jpa.open-in-view = true
這意味着交易始終是開放的。 嘗試禁用它。
更多信息在這里

使用調試器時,您正在嘗試訪問變量的值。 因此,當您單擊屏幕上的那個小箭頭時,相關變量的值就被(延遲地)加載了。

只是猜測:您在構建規范時強制獲取。

我希望像

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

如果是這種情況,請嘗試將root.fetch更改為root.join

檢索到的數據已經延遲,但您正在使用調試模式,當單擊從調試器查看數據時,它的返回值。

你可以用jackson-datatype-hibernate用 2 個步驟來解決這個問題:

科特林示例

  1. 添加build.gradle.kts
implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
  1. 創建@Bean
   @Bean
   fun hibernate5Module(): Module = Hibernate5Module()

請注意, Modulecom.fasterxml.jackson.databind.Module ,而不是java.util.Module

另一個考慮是在使用 Lombok 時,@Data/@Getter 注釋會導致不需要加載惰性項。 所以在使用 Lombok 時要小心。

這是我的情況。

我想您正在使用 Hibernate 作為 JPA。

從規格:

EAGER 策略是對持久性提供程序運行時的要求,即必須急切地獲取數據。 LAZY 策略是對持久性提供程序運行時的提示,即在首次訪問數據時應該延遲獲取數據。 允許該實現急切地獲取已指定 LAZY 策略提示的數據。 https://docs.jboss.org/hibernate/jpa/2.2/api/javax/persistence/FetchType.html

Hibernate 在非擁有方的 OneToOne 和 ManyToOne 關系中特別忽略 fetch 類型。

如果你真的需要它,如何強制 Hibernate 使用獲取類型 LAZY 的選項很少。

  1. 最簡單的一種是偽造一對多的關系。 這將起作用,因為集合的延遲加載比單個可空屬性的延遲加載要容易得多,但如果您使用復雜的 JPQL/HQL 查詢,通常這種解決方案非常不方便。
  2. 另一種是使用構建時字節碼檢測。 有關更多詳細信息,請閱讀 Hibernate 文檔:19.1.7。 使用惰性屬性獲取。 請記住,在這種情況下,您必須將 @LazyToOne(LazyToOneOption.NO_PROXY) 注釋添加到一對一關系以使其變得惰性。 將 fetch 設置為 LAZY 是不夠的。
  3. 最后一個解決方案是使用運行時字節碼檢測,但它僅適用於在成熟的 JEE 環境中使用 Hibernate 作為 JPA 提供程序的人(在這種情況下,將“hibernate.ejb.use_class_enhancer”設置為 true 應該可以解決問題:Entity Manager Configuration ) 或使用 Hibernate 和 Spring 配置以進行運行時編織(這在某些較舊的應用程序服務器上可能難以實現)。 在這種情況下,還需要 @LazyToOne(LazyToOneOption.NO_PROXY) 注釋。

有關更多信息,請查看: http://justonjava.blogspot.com/2010/09/lazy-one-to-one-and-one-to-many.html

我想我可能有一個解決方案。 你可以試試這個。 經過 4 小時的打擊和試用后,這對我有用-

用戶實體:

class User {
    @Id
    String id;

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

地址實體:

class Address {

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

您的父 class 將使用 @JsonManagedReference,子 class 將使用 @JsonBackReference。 這樣,您可以避免實體對象的無限循環作為響應和堆棧溢出錯誤。

我也遇到了與 Spring 數據 JPA 相同的問題。 我添加了以下注釋並能夠獲取給定 ORDER ID 的客戶記錄

客戶訂購:一對多

給客戶的訂單是延遲加載。

訂單.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

客戶.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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM