如何解决JPA:在JPQL中使用构造函数表达式在DTO中包装单个实体时,绕开N + 1
首先,我知道(Prevent "n+1 selects" with JPA/Hibernate constructor expression?)处的类似问题,但我的情况有所不同,其他问题没有足够的答案。
我注意到当我使用 Hibernate 作为我的 JPA 提供程序进行 JPQL 查询时,我尝试将一个实体包装到 DTO 通过使用构造函数表达式, Hibernate 似乎仅选择实体的ID,此ID随后会导致N + 1查询以获取其余属性。
休眠版本: 5.4.21。最终版本
查询:
select new com.example.distanceResult(p,distance(:center,p.location)) from Place p where dwithin(:center,p.location,:radiusMeters) = true
查询日志:
select place1_.id as col_0_0_,st_distance(?,place0_.location) as col_1_0_ from places place0_ inner join places place1_ on (place0_.id=place1_.id) where st_dwithin(?,place0_.location,?)=true
binding parameter [1] as [OTHER] - [POINT (10.90943 48.37102)]
binding parameter [2] as [OTHER] - [POINT (10.90943 48.37102)]
binding parameter [3] as [DOUBLE] - [2000.0]
select place0_.id as id1_0_0_,place0_.location as location2_0_0_,place0_.name as name3_0_0_ from places place0_ where place0_.id=?
binding parameter [1] as [BIGINT] - [3]
select place0_.id as id1_0_0_,place0_.name as name3_0_0_ from places place0_ where place0_.id=?
binding parameter [1] as [BIGINT] - [4]
如您所见,我正在尝试使用 hibernate-spatial 进行“位于地理位置周围的实体”类型的查询,但我认为这与问题无关。如何告诉休眠选择整个实体,然后将其包装到 DTO 中?我已经尝试进行获取自连接,但这没有帮助。
java类:(使用Lombok)
@Value
public class distanceResult {
Place place;
Double distanceMeters;
}
@NoArgsConstructor
@Getter
@Setter
@ToString
@Entity
@Table(name = "PLACES")
public class Place extends BaseEntity<Long> {
public Place(String name,Point location) {
this.name = name;
this.location = location;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Point location;
}
BaseEntity
与 spring-data-jpa 中的AbstractPersistable
差不多,没有ID定义。
解决方法
好吧,我找到了一个解决方案,但是不确定这是否是最好的解决方法。我没有使用 JPQL 中的构造函数表达式,而是使用了(休眠专用的)ResultTransformer
。
代码:
public List<DistanceResult> findInDistance(Point center,double radiusMeters) {
String jpql = "select p,distance(:center,p.location) from Place p where dwithin(:center,p.location,:radiusMeters) = true";
return (List<DistanceResult>) entityManager.createQuery(jpql)
.setParameter("center",center)
.setParameter("radiusMeters",radiusMeters)
.unwrap(Query.class)
.setResultTransformer(
(ListResultTransformer)
(tuple,aliases) -> new DistanceResult(
(Place) tuple[0],((Number) tuple[1]).doubleValue()
)
).getResultList();
}
注意: ListResultTransformer
是Vlad Mihalcea的休眠类型库的一部分。 (另请参见:https://cloud.google.com/run/docs/issues#ah)
查询日志:
select place0_.id as col_0_0_,st_distance(?,place0_.location) as col_1_0_,place0_.id as id1_0_,place0_.location as location2_0_,place0_.name as name3_0_ from places place0_ where st_dwithin(?,place0_.location,?)=true
binding parameter [1] as [OTHER] - [POINT (10.90943 48.37102)]
binding parameter [2] as [OTHER] - [POINT (10.90943 48.37102)]
binding parameter [3] as [DOUBLE] - [2000.0]
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。