[英]Spring JPA REST sort by nested property
我有實體Market
和Event
。 Market
實體有一欄:
@ManyToOne(fetch = FetchType.EAGER)
private Event event;
接下來我有一個存儲庫:
public interface MarketRepository extends PagingAndSortingRepository<Market, Long> {
}
和一個投影:
@Projection(name="expanded", types={Market.class})
public interface ExpandedMarket {
public String getName();
public Event getEvent();
}
使用 REST 查詢/api/markets?projection=expanded&sort=name,asc
我成功獲得了具有按市場名稱排序的嵌套事件屬性的市場列表:
{
"_embedded" : {
"markets" : [ {
"name" : "Match Odds",
"event" : {
"id" : 1,
"name" : "Watford vs Crystal Palace"
},
...
}, {
"name" : "Match Odds",
"event" : {
"id" : 2,
"name" : "Arsenal vs West Brom",
},
...
},
...
}
}
但我需要的是獲取按事件名稱排序的市場列表,我嘗試了查詢/api/markets?projection=expanded&sort=event.name,asc
但它沒有用。 我應該怎么做才能讓它工作?
只需將spring.data.rest.webmvc
降級到Hopper
版本
<spring.data.jpa.version>1.10.10.RELEASE</spring.data.jpa.version>
<spring.data.rest.webmvc.version>2.5.10.RELEASE</spring.data.rest.webmvc.version>
projection=expanded&sort=event.name,asc // works
projection=expanded&sort=event_name,asc // this works too
在 Hopper 版本中按嵌套屬性排序對我來說效果很好,但我確實在 Ingalls 版本的 RC 版本中遇到了以下錯誤。 這被報告為已修復,
順便說一句,我嘗試了v3.0.0.M3
,它報告說已修復但對我不起作用。
關於這個問題有什么消息嗎? 同樣在Spring Boot 2.1和Spring Data Lovelace版本中,無法按嵌套屬性進行排序。
您的MarketRepository
可以有一個named query
如:
public interface MarketRepository exten PagingAndSortingRepository<Market, Long> {
Page<Market> findAllByEventByName(String name, Page pageable);
}
您可以使用@RequestParam
從 url 中獲取您的name
參數
這個頁面有一個可行的想法。 這個想法是在存儲庫頂部使用控制器,並單獨應用投影。
這是一段有效的代碼(SpringBoot 2.2.4)
import ro.vdinulescu.AssignmentsOverviewProjection;
import ro.vdinulescu.repository.AssignmentRepository;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.PagedModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RepositoryRestController
public class AssignmentController {
@Autowired
private AssignmentRepository assignmentRepository;
@Autowired
private ProjectionFactory projectionFactory;
@Autowired
private PagedResourcesAssembler<AssignmentsOverviewProjection> resourceAssembler;
@GetMapping("/assignments")
public PagedModel<EntityModel<AssignmentsOverviewProjection>> listAssignments(@RequestParam(required = false) String search,
@RequestParam(required = false) String sort,
Pageable pageable) {
// Spring creates the Pageable object correctly for simple properties,
// but for nested properties we need to fix it manually
pageable = fixPageableSort(pageable, sort, Set.of("client.firstName", "client.age"));
Page<Assignment> assignments = assignmentRepository.filter(search, pageable);
Page<AssignmentsOverviewProjection> projectedAssignments = assignments.map(assignment -> projectionFactory.createProjection(
AssignmentsOverviewProjection.class,
assignment));
return resourceAssembler.toModel(projectedAssignments);
}
private Pageable fixPageableSort(Pageable pageable, String sortStr, Set<String> allowedProperties) {
if (!pageable.getSort().equals(Sort.unsorted())) {
return pageable;
}
Sort sort = parseSortString(sortStr, allowedProperties);
if (sort == null) {
return pageable;
}
return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
}
private Sort parseSortString(String sortStr, Set<String> allowedProperties) {
if (StringUtils.isBlank(sortStr)) {
return null;
}
String[] split = sortStr.split(",");
if (split.length == 1) {
if (!allowedProperties.contains(split[0])) {
return null;
}
return Sort.by(split[0]);
} else if (split.length == 2) {
if (!allowedProperties.contains(split[0])) {
return null;
}
return Sort.by(Sort.Direction.fromString(split[1]), split[0]);
} else {
return null;
}
}
}
來自 Spring Data REST 文檔:
不支持按可鏈接關聯(即頂級資源的鏈接)排序。
https://docs.spring.io/spring-data/rest/docs/current/reference/html/#paging-and-sorting.sorting
我發現的另一種方法是使用@ResResource(exported=false)
。 這是無效的(特別是對於遺留的 Spring Data REST 項目),因為避免資源/實體將被加載 HTTP 鏈接:
JacksonBinder
BeanDeserializerBuilder updateBuilder throws
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of ' com...' no String-argument constructor/factory method to deserialize from String value
我試圖激活排序聯協會與注釋的幫助,但沒有成功,因為我們需要總是需要重寫mappPropertyPath
的方法JacksonMappingAwareSortTranslator.SortTranslator
檢測注釋:
if (associations.isLinkableAssociation(persistentProperty)) {
if(!persistentProperty.isAnnotationPresent(SortByLinkableAssociation.class)) {
return Collections.emptyList();
}
}
注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SortByLinkableAssociation {
}
在您的項目中,在可鏈接的關聯中包含@SortByLinkableAssociation排序。
@ManyToOne(fetch = FetchType.EAGER)
@SortByLinkableAssociation
private Event event;
真的,我沒有找到解決這個問題的明確和成功的解決方案,但決定公開它讓思考它甚至 Spring 團隊考慮將其包含在下一個版本中。
我們有一個案例,當我們想按鏈接實體中的字段排序(這是一對一的關系)。 最初,我們使用基於https://stackoverflow.com/a/54517551 的示例按鏈接字段進行搜索。
因此,我們案例中的解決方法/黑客是提供自定義排序和可分頁參數。 下面是示例:
@org.springframework.data.rest.webmvc.RepositoryRestController
public class FilteringController {
private final EntityRepository repository;
@RequestMapping(value = "/entities",
method = RequestMethod.GET)
public ResponseEntity<?> filter(
Entity entity,
org.springframework.data.domain.Pageable page,
org.springframework.data.web.PagedResourcesAssembler assembler,
org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler entityAssembler,
org.springframework.web.context.request.ServletWebRequest webRequest
) {
Method enclosingMethod = new Object() {}.getClass().getEnclosingMethod();
Sort sort = new org.springframework.data.web.SortHandlerMethodArgumentResolver().resolveArgument(
new org.springframework.core.MethodParameter(enclosingMethod, 0), null, webRequest, null
);
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreCase()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
Example example = Example.of(entity, matcher);
Page<?> result = this.repository.findAll(example, PageRequest.of(
page.getPageNumber(),
page.getPageSize(),
sort
));
PagedModel search = assembler.toModel(result, entityAssembler);
search.add(linkTo(FilteringController.class)
.slash("entities/search")
.withRel("search"));
return ResponseEntity.ok(search);
}
}
使用的Spring boot版本:2.3.8.RELEASE
我們還有實體的存儲庫並使用了投影:
@RepositoryRestResource
public interface JpaEntityRepository extends JpaRepository<Entity, Long> {
}
基於 Spring Data JPA 文檔4.4.3。<\/a> 屬性表達式<\/a>
...您可以在方法名稱中使用 _ 手動定義遍歷點...
您可以將下划線放在 REST 查詢中,如下所示:
\/api\/markets?projection=expanded&sort= event_name<\/strong> ,asc
我有實體Market
和Event
。 Market
實體具有一列:
@ManyToOne(fetch = FetchType.EAGER)
private Event event;
接下來,我有一個存儲庫:
public interface MarketRepository extends PagingAndSortingRepository<Market, Long> {
}
和一個投影:
@Projection(name="expanded", types={Market.class})
public interface ExpandedMarket {
public String getName();
public Event getEvent();
}
使用REST查詢/api/markets?projection=expanded&sort=name,asc
我成功地獲得了具有按市場名稱排序的嵌套事件屬性的市場列表:
{
"_embedded" : {
"markets" : [ {
"name" : "Match Odds",
"event" : {
"id" : 1,
"name" : "Watford vs Crystal Palace"
},
...
}, {
"name" : "Match Odds",
"event" : {
"id" : 2,
"name" : "Arsenal vs West Brom",
},
...
},
...
}
}
但是我需要獲取按事件名稱排序的市場列表,我嘗試了查詢/api/markets?projection=expanded&sort=event.name,asc
但沒有成功。 我應該怎么做才能使其正常工作?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.