簡體   English   中英

如何在避免 n+1 查詢的同時設置 JPA 雙向一對一

[英]How to setup JPA bi-directional one to one while avoiding n+1 queries

我正在嘗試調試具有雙向一對一關系的兩個實體。 問題在於該關系導致發生 N+1 查詢。 當我運行findAll()時,會針對我表中的每個項目執行單個查詢。

這是一個例子:我有兩個實體

package ...;
import ...;

@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor
@EqualsAndHashCode
@Getter
@Builder

@Entity
@Table(name = "tracker")
public class TrackerEntity {
    @Id
    @Column(name = "vehicleid")
    private UUID vehicleId;

    @Column(name = "trackerid")
    private String trackerId;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "vehicleid", referencedColumnName = "id")
    private VehicleEntity vehicleEntity;
}

@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor
@EqualsAndHashCode
@Getter
@Builder

@Entity
@Table(name = "vehicle")
public class VehicleEntity {
    @Id
    private UUID id;

    @Column(name = "vehiclename")
    private String vehicleName;

    @OneToOne(mappedBy = "vehicleEntity", cascade = CascadeType.REMOVE, orphanRemoval = true)
    private TrackerEntity trackerEntity;
}

當我運行vehicleRepository.findAll()時,我得到以下調試日志:

2022-05-17 19:16:45.071 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_0_, vehicleent0_.vehiclename as vehiclen2_1_0_ from vehicle vehicleent0_ where vehicleent0_.id=?
2022-05-17 19:16:45.097 DEBUG 62639 --- [           main] org.hibernate.SQL                        : insert into vehicle (vehiclename, id) values (?, ?)
2022-05-17 19:16:45.104 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_0_, vehicleent0_.vehiclename as vehiclen2_1_0_ from vehicle vehicleent0_ where vehicleent0_.id=?
2022-05-17 19:16:45.106 DEBUG 62639 --- [           main] org.hibernate.SQL                        : insert into vehicle (vehiclename, id) values (?, ?)
2022-05-17 19:16:45.109 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_0_, vehicleent0_.vehiclename as vehiclen2_1_0_ from vehicle vehicleent0_ where vehicleent0_.id=?
2022-05-17 19:16:45.110 DEBUG 62639 --- [           main] org.hibernate.SQL                        : insert into vehicle (vehiclename, id) values (?, ?)
2022-05-17 19:16:45.113 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_0_, vehicleent0_.vehiclename as vehiclen2_1_0_ from vehicle vehicleent0_ where vehicleent0_.id=?
2022-05-17 19:16:45.115 DEBUG 62639 --- [           main] org.hibernate.SQL                        : insert into vehicle (vehiclename, id) values (?, ?)
2022-05-17 19:16:45.117 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_0_, vehicleent0_.vehiclename as vehiclen2_1_0_ from vehicle vehicleent0_ where vehicleent0_.id=?
2022-05-17 19:16:45.119 DEBUG 62639 --- [           main] org.hibernate.SQL                        : insert into vehicle (vehiclename, id) values (?, ?)
2022-05-17 19:16:45.123 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_0_, vehicleent0_.vehiclename as vehiclen2_1_0_ from vehicle vehicleent0_ where vehicleent0_.id=?
2022-05-17 19:16:45.125 DEBUG 62639 --- [           main] org.hibernate.SQL                        : insert into vehicle (vehiclename, id) values (?, ?)
2022-05-17 19:16:45.128 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_0_, vehicleent0_.vehiclename as vehiclen2_1_0_ from vehicle vehicleent0_ where vehicleent0_.id=?
2022-05-17 19:16:45.130 DEBUG 62639 --- [           main] org.hibernate.SQL                        : insert into vehicle (vehiclename, id) values (?, ?)
2022-05-17 19:16:45.133 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_0_, vehicleent0_.vehiclename as vehiclen2_1_0_ from vehicle vehicleent0_ where vehicleent0_.id=?
2022-05-17 19:16:45.135 DEBUG 62639 --- [           main] org.hibernate.SQL                        : insert into vehicle (vehiclename, id) values (?, ?)
2022-05-17 19:16:45.138 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_0_, vehicleent0_.vehiclename as vehiclen2_1_0_ from vehicle vehicleent0_ where vehicleent0_.id=?
2022-05-17 19:16:45.141 DEBUG 62639 --- [           main] org.hibernate.SQL                        : insert into vehicle (vehiclename, id) values (?, ?)
2022-05-17 19:16:45.144 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_0_, vehicleent0_.vehiclename as vehiclen2_1_0_ from vehicle vehicleent0_ where vehicleent0_.id=?
2022-05-17 19:16:45.146 DEBUG 62639 --- [           main] org.hibernate.SQL                        : insert into vehicle (vehiclename, id) values (?, ?)
2022-05-17 19:16:45.246 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select vehicleent0_.id as id1_1_, vehicleent0_.vehiclename as vehiclen2_1_ from vehicle vehicleent0_
2022-05-17 19:16:45.253 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select trackerent0_.vehicleid as vehiclei1_0_0_, trackerent0_.trackerid as trackeri2_0_0_ from tracker trackerent0_ where trackerent0_.vehicleid=?
2022-05-17 19:16:45.258 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select trackerent0_.vehicleid as vehiclei1_0_0_, trackerent0_.trackerid as trackeri2_0_0_ from tracker trackerent0_ where trackerent0_.vehicleid=?
2022-05-17 19:16:45.259 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select trackerent0_.vehicleid as vehiclei1_0_0_, trackerent0_.trackerid as trackeri2_0_0_ from tracker trackerent0_ where trackerent0_.vehicleid=?
2022-05-17 19:16:45.261 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select trackerent0_.vehicleid as vehiclei1_0_0_, trackerent0_.trackerid as trackeri2_0_0_ from tracker trackerent0_ where trackerent0_.vehicleid=?
2022-05-17 19:16:45.262 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select trackerent0_.vehicleid as vehiclei1_0_0_, trackerent0_.trackerid as trackeri2_0_0_ from tracker trackerent0_ where trackerent0_.vehicleid=?
2022-05-17 19:16:45.264 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select trackerent0_.vehicleid as vehiclei1_0_0_, trackerent0_.trackerid as trackeri2_0_0_ from tracker trackerent0_ where trackerent0_.vehicleid=?
2022-05-17 19:16:45.265 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select trackerent0_.vehicleid as vehiclei1_0_0_, trackerent0_.trackerid as trackeri2_0_0_ from tracker trackerent0_ where trackerent0_.vehicleid=?
2022-05-17 19:16:45.267 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select trackerent0_.vehicleid as vehiclei1_0_0_, trackerent0_.trackerid as trackeri2_0_0_ from tracker trackerent0_ where trackerent0_.vehicleid=?
2022-05-17 19:16:45.269 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select trackerent0_.vehicleid as vehiclei1_0_0_, trackerent0_.trackerid as trackeri2_0_0_ from tracker trackerent0_ where trackerent0_.vehicleid=?
2022-05-17 19:16:45.271 DEBUG 62639 --- [           main] org.hibernate.SQL                        : select trackerent0_.vehicleid as vehiclei1_0_0_, trackerent0_.trackerid as trackeri2_0_0_ from tracker trackerent0_ where trackerent0_.vehicleid=?

我搜索了 N+1 JPA 查詢問題,希望找到解決方案,大多數建議使用 EntityGraph 或“字節碼增強”。 我已經嘗試使用實體圖,但仍然有 n+1 個查詢,我不想使用字節碼增強,因為應用程序必須作為微服務在雲上運行。

更重要的一件事是我希望在移除車輛時刪除跟蹤器,但不應在創建車輛時創建跟蹤器。

有沒有什么辦法解決這一問題?

我試過使用 Query 和 join fetch:

public interface VehicleRepository extends JpaRepository<VehicleEntity, UUID> {

    @Override
    @Query("SELECT v FROM VehicleEntity v left outer join fetch v.trackerEntity t")
    <S extends VehicleEntity> List<S> findAll(Example<S> example);
}

這並沒有改變任何東西,仍然有 n+1 個查詢。 我試過 EntityGraph 也沒有改變任何東西:

@Entity
@Table(name = "vehicle")
@NamedEntityGraph(name="vehicleGraph", attributeNodes = {@NamedAttributeNode("trackerEntity")})
public class VehicleEntity {
    @Id
    private UUID id;

    @Column(name = "vehiclename")
    private String vehicleName;

    @OneToOne(mappedBy = "vehicleEntity", cascade = CascadeType.REMOVE, orphanRemoval = true)
    private TrackerEntity trackerEntity;
}
public interface VehicleRepository extends JpaRepository<VehicleEntity, UUID> {

    @Override
    @EntityGraph(value = "vehicleGraph", type = EntityGraph.EntityGraphType.LOAD)
    <S extends VehicleEntity> List<S> findAll(Example<S> example);
}

您可以使用EntityGraph和延遲加載對其進行優化。 我假設VehicleEntity是這里的擁有實體。 在那種情況下, make fetch = FetchType.LAZYcascade = CascadeType.ALL應該在擁有的實體上。 EntityGraph 可以通過兩種方式定義

  1. 命名實體圖
  2. AD-HOC 或未命名實體圖

命名實體圖:

您可以在擁有 class 時定義NamedEntityGraph並在VehicleEntityRepository接口中引用它

@NamedEntityGraph(name = "VehicleEntity.trackerEntity", attributeNodes = @NamedAttributeNode("trackerEntity"))
public class VehicleEntity {
}

VehicleEntityRepository.java

public interface VehicleEntityRepository extends JpaRepository<VehicleEntity, UUID> {
     @EntityGraph(value = "VehicleEntity.trackerEntity", type = EntityGraphType.LOAD)
    List<VehicleEntity> findAll();
}

AD-HOC 實體圖:

您不必定義任何 EntityGraph 注釋 VehicleEntity class 而是可以直接使用它 Repository 接口

VehicleEntityRepository.java

public interface VehicleEntityRepository extends JpaRepository<VehicleEntity, UUID> {
    @EntityGraph(attributePaths = { "trackerEntity" })
    List<VehicleEntity> findAll();
}

VehicleEntity.java

@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor
@EqualsAndHashCode
@Getter
@Builder

@Entity
@Table(name = "vehicle")
@NamedEntityGraph(name = "VehicleEntity.trackerEntity", attributeNodes = @NamedAttributeNode("trackerEntity"))
public class VehicleEntity {
    @Id
    private UUID id;

    @Column(name = "vehiclename")
    private String vehicleName;

 
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "vehicleEntity")
    @JsonManagedReference
    private TrackerEntity trackerEntity;
}

TrackerEntity.java

package ...;
import ...;

@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor
@EqualsAndHashCode
@Getter
@Builder

@Entity
@Table(name = "tracker")
public class TrackerEntity {
    @Id
    @Column(name = "id")
    private UUID id;

    @Column(name = "trackerid")
    private String trackerId;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "vehicleid", referencedColumnName = "id")
@JsonBackReference
    private VehicleEntity vehicleEntity;
}
    

在使用 Jackson 時,您還可以使用@JsonManagedReference@JsonBackReference來防止嵌套獲取或無限遞歸。

暫無
暫無

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

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