Spring Pageable不翻译@Column名称

时间:2018-07-21 20:05:23

标签: java spring-boot spring-data-jpa spring-rest

我有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查询中。

有什么方法可以翻译可分页的排序,以便它不使用字段名而是使用列名?

2 个答案:

答案 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));
    }