我想提取物體,它只包含 DB 中資料的特定部分。但是 Hibernate 創建了額外的查詢,完全填充了我的物體。
例子
偽查詢:Select * From ShopUnitDB WHERE prices.date BETWEEN BETWEEN 2022-05-02 AND 2022-05-30;
偽預期:ShopUnit{1, Name, prices=[{2000, 2022-05-25}, {4000, 2022-05-29}]}
結果:ShopUnitPrice 表中與 ShopUnitDB 相關的所有資料。
控制臺日志:
Hibernate:選擇 shopunitdb0_.id 作為 id1_0_,shopunitdb0_.name 作為 name2_0_,shopunitdb0_.parent_id 作為 parent_i3_0_,shopunitdb0_.type 作為 type4_0_ 從 shop_unit shopunitdb0_ 內部連接 ??shop_unit_priceprices1_ on shopunitdb0_.id=prices1_.unit_id where shopunitdb0_.id=? 和(prices1_.date 介于 ? 和 ? 之間)
休眠:從shop_unit_priceprices0_中選擇prices0_.unit_id作為unit_id4_1_0_,price0_.id作為id1_1_0_,prices0_.id作為id1_1_1_,prices0_.date作為date2_1_1_,prices0_.price作為price3_1_1_,prices0_.unit_id作為unit_id4_1_1_ from shop_unit_priceprices0_ where prices0_.unit_id=?
看,第一個查詢提取正確的物體 - 僅包含基于 WHERE ... BETWEEN 陳述句的部分。接下來,它創建新查詢以提取 ShopUnitDB 子物體的所有資料。
我使用 Spring Data JPA 規范。還嘗試@Query在 JpaRepository 中使用。沒運氣。
物體 1:
@Entity
@Table(name = "shop_unit")
public class ShopUnitDB {
@Id
private UUID id;
@Column(name = "name")
private String name;
@Column(name = "parent_id")
@Nullable
private UUID parentId;
@Enumerated(EnumType.STRING)
private ShopUnitType type;
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "unit_id", referencedColumnName = "id")
private List<ShopUnitPrice> prices;
@Transient
private Set<ShopUnitDB> children;
...
public List<ShopUnit> convertToShopUnitStatistic() {
return prices.stream()
.map(price -> new ShopUnit(id, name, price.getDate(),
parentId, type, price.getPrice()))
.collect(Collectors.toList());
}
...
}
物體 2:
@Entity
@Table(name = "shop_unit_price")
public class ShopUnitPrice {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "unit_id")
private UUID unitId;
@Column(name = "date")
private LocalDateTime date;
@Column(name = "price")
private Integer price;
...
}
我的服務,使用 Spring Data JPA 規范呼叫 JpaRepository:
@Service
public class ShopUnitService {
private ShopUnitRepository jpa;
@Autowired
public ShopUnitService(ShopUnitRepository jpa) {
this.jpa = jpa;
}
public List<ShopUnit> getShopUnitStatistic(UUID uuid, LocalDateTime start, LocalDateTime end)
{
unitDB = jpa.findAll(where(idLike(uuid).and(joinPrices(start, end)))).get(0);
return unitDB.convertToShopUnitStatistic();
}
private Specification<ShopUnitDB> idLike(UUID id) {
return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("id"), id);
}
private Specification<ShopUnitDB> joinPrices(LocalDateTime start, LocalDateTime end) {
return (root, query, criteriaBuilder) -> {
return criteriaBuilder.between(root.join("prices").get("date"), start, end);
};
}
}
Spring JpaRepository:
@Repository
public interface ShopUnitRepository extends JpaRepository<ShopUnitDB, UUID>, JpaSpecificationExecutor<ShopUnitDB> {}
更新
spring-boot-starter-parent 版本:2.6.8
應用程式屬性
server.port=80
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/goods_warehouse
spring.datasource.username=username
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.open-in-view=false
spring.jpa.show-sql=true
uj5u.com熱心網友回復:
我看到您在物體和標準 API 中使用fetch = FetchType.EAGER關聯OneToMany(不推薦)ShopUnitDB來獲取資料。如果您使用 JPQL/Criteria API 來獲取 fetchType=EAGER 的資料,它將觸發 2 個查詢來檢索資料。請參閱此鏈接了解更多詳情。
要解決您的問題,我認為您應該從OneToMany關聯中洗掉 fetchTypeShopUnitDB或使其成為fetchType = FetchType.LAZY
uj5u.com熱心網友回復:
根據@Daniel Wosch的評論,我理解了主要問題。為了解決它,我曾經像這樣使用 JPARepository 方法@Query:
@Repository
public interface ShopUnitRepository extends JpaRepository<ShopUnitDB, UUID>, JpaSpecificationExecutor<ShopUnitDB> {
@Query("SELECT unit "
"FROM ShopUnitDB unit JOIN FETCH unit.prices price "
"WHERE unit.id = ?1 AND price.date BETWEEN ?2 AND ?3")
ShopUnitDB findByIdAndDate(UUID uuid, LocalDateTime dateStart, LocalDateTime dateEnd);
}
請注意,我使用JOIN FETCH *ShopUnit collection field*并過濾了該資料集。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/494635.html
下一篇:帶有分離標準謂詞的Grails3
