我有Entity对象:
@Entity(name = "table")
public class SomeEntity {
@Id
@Column(name = "id_column_name")
public final BigDecimal entityId;
@Column(name = "table_column_name")
public final String entityFieldName;
}
我有这样定义的数据库视图:
CREATE OR REPLACE FORCE EDITIONABLE VIEW "V_TABLE" ("ID_COLUMN_NAME", "TABLE_COLUMN_NAME", "SOME_OTHER_COLUMN") AS ... (some SQL magic)
我有带有自定义查询的存储库:
@RepositoryRestResource
interface SomeEntityRepository extends PagingAndSortingRepository<SomeEntity, BigDecimal> {
@Query(value = "select id_column_name, table_column_name FROM V_TABLE where some_other_column = ?#{#parameter} order by ?#{#pageable}",
countQuery = "SELECT count(*) from V_TABLE v where some_other_column = ?#{#parameter}",
nativeQuery = true)
Page<SomeEntity> findBySomeParameter(@Param("parameter") long parameter, Pageable pageable);
}
当我使用url请求标准数据时,一切工作正常:
http://localhost:8080/someEntity/search/findBySomeParameter?parameter=25&page=0&size=20
但是当我添加排序信息时,它不起作用:
http://localhost:8080/someEntity/search/findBySomeParameter?parameter=25&page=0&size=20&sort=entityFieldName,asc
将引发以下异常(我正在使用Oracle数据库):
Caused by: java.sql.SQLSyntaxErrorException: ORA-00904: "ENTITYFIELDNAME": invalid identifier
似乎排序字段未使用@Column(name)
进行翻译,而是内联到SQL查询中。
有什么方法可以翻译可分页的排序,以便它不使用字段名而是使用列名?
答案 0 :(得分:2)
此article阐明了这个问题。请阅读第3.1节。
显然,本机查询不支持动态排序。实际上,如果将findBySomeParameter方法更改为采用Sort
而不是Pageable
,则会得到org.springframework.data.jpa.repository.query.InvalidJpaQueryMethodException: Cannot use native queries with dynamic sorting
。
使用pageable不会出现异常,分页实际上似乎可以正常工作,但是动态排序不能替代您发现的列名。在我看来,唯一的解决方案是使用JPQL而不是本机查询,只要您要进行的查询就是您提供的查询,就没有问题。为了使用JPQL,您需要将视图映射到SomeEntityView类。
编辑 我认为该问题没有记录在案,但实际上在official doc
中Spring Data JPA当前不支持对本机查询进行动态排序,因为它必须操纵声明的实际查询,而这对于本机SQL无法可靠地完成。但是,您可以自己指定计数查询,从而将本机查询用于分页,如以下示例所示:
答案 1 :(得分:0)
此解决方法适用于 SpringBoot 2.4.3:
@PersistenceContext
private EntityManager entityManager;
// an object ptoperty name to a column name adapter
private Pageable adaptSortColumnNames(Pageable pageable) {
if (pageable.getSort().isSorted()) {
SessionFactory sessionFactory;
if (entityManager == null || (sessionFactory = entityManager.getEntityManagerFactory().unwrap(SessionFactory.class)) == null)
return pageable;
AbstractEntityPersister persister = (AbstractEntityPersister) ((MetamodelImplementor) sessionFactory.getMetamodel()).entityPersister(CommentEntity.class);
Sort adaptedSort = pageable.getSort().get().limit(1).map(order -> {
String propertyName = order.getProperty();
String columnName = persister.getPropertyColumnNames(propertyName)[0];
return Sort.by(order.getDirection(), columnName);
}).findFirst().get();
return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), adaptedSort);
}
return pageable;
}
@GetMapping()
public ResponseEntity<PagedResponse<CommentResponse>> findByTextContainingFts(@RequestParam(value = "text", required = false) String text, Pageable pageable) {
// apply this adapter in controller
pageable = adaptSortColumnNames(pageable);
Page<CommentEntity> page = commentRepository.find(text, pageable);
return ResponseEntity.ok().body(domainMapper.fromPageToPagedResponse(page));
}