Not exposing the path of entities that have a composite primary key to the front end when using the Springframework Page object

你离开我真会死。 提交于 2021-02-08 07:29:33

问题


I'm working on an API endpoint that returns a Springframework Page response. I want the front end to be able to sort the data but I can't expect the front end to know that the column they want to sort on is actually inside a composite primary key.

In the example below (a simplified version of what I'm working on) you can see that the startDate column is inside a RouteEntityPk class, which is linked to the RouteEntity class with the @EmbeddedId annotation. To Sort on that column the front end would need to add ?sort=pk.startdate,asc to the request. I want the front end to only have to provide ?sort=startdate,asc.

Is there a way - using Spring magic - of having the repository know that startdate == pk.startdate, or will I have to write a translator which will remove the pk when showing the sort column to the front end, and add it where necessary when reading it from the request?

Controller:

@GetMapping(value = "routes/{routeId}", produces = APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Page<Route>> getRouteByRouteId(@PathVariable(value = "routeId") final String routeId,
                                                     @PageableDefault(size = 20) @SortDefault.SortDefaults({
                                                            @SortDefault(sort = "order", direction = Sort.Direction.DESC),
                                                            @SortDefault(sort = "endDate", direction = Sort.Direction.DESC)
                                                     }) final Pageable pageable) {

    return ResponseEntity.ok(routeService.getRouteByRouteId(routeId, pageable));
}

Service:

public Page<Route> getRouteByRouteId(String routeId, Pageable pageable) {

    Page<RouteEntity> routeEntities = routeRepository.findByRouteId(routeId, pageable);

    return new PageImpl<>(
            Collections.singletonList(routeTransformer.toRoute(routeId, routeEntities)),
            pageable,
            routeEntities.getContent().size()
    );
}

Repository:

@Repository
public interface RouteRepository extends JpaRepository<RouteEntity, RouteEntityPk> {

    @Query(value = " SELECT re FROM RouteEntity re"
                 + " AND re.pk.routeId = :routeId")
    Page<RouteEntity> findByRouteId(@Param("routeId") final String routeId,
                                    Pageable pageable);
}

Entities:

Route:

@Data
@Entity
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "ROUTE", schema = "NAV")
public class RouteEntity {

    @EmbeddedId
    private RouteEntityPk pk;

    @Column(name = "NAME")
    private String name;

    @Column(name = "ORDER")
    private Integer order;

    @Column(name = "END_DTE")
    private LocalDate endDate;
}

RoutePk:

@Data
@Builder(toBuilder = true)
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
public class RouteEntityPk implements Serializable {

    private static final long serialVersionUID = 1L;

    @Column(name = "ROUTE_ID")
    private String routeId;

    @Column(name = "STRT_DTE")
    private LocalDate startDate;
}

Models:

Route:

@Data
@Builder
public class Route {

    public String name;
    public String routeId;

    public List<RouteItem> items;
}

Item:

@Data
@Builder
public class Item {
    public Integer order;
    public LocalDate startDate;
    public LocalDate endDate;
}

Transformer:

public Route toRoute(String routeId, Page<RouteEntity> routeEntities) {

    return Route.builder()
            .name(getRouteName(routeEntities))
            .routeId(routeId)
            .items(routeEntities.getContent().stream()
                    .map(this::toRouteItem)
                    .collect(Collectors.toList()))
            .build();
}

private Item toRouteItem(RouteEntity item) {

    return ParcelshopDrop.builder()
            .order(item.getOrder())
            .startDate(item.getStartDate())
            .endDate(item.getEndDate())
            .build();
}

回答1:


So it looks like the way to do this is to use the other way you can deal with composite primary key's in JPA, the annotation @IdClass. This way you can put the fields in the main entity and refer to them as such.

Below is a link to the baeldung article I followed and the changes to the entities I posted above that make this work:

https://www.baeldung.com/jpa-composite-primary-keys

Entities:

Route:

@Data
@Entity
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@IdClass(RouteEntityPk.class)
@Table(name = "ROUTE", schema = "NAV")
public class RouteEntity {

    @Id
    @Column(name = "ROUTE_ID")
    private String routeId;

    @Id
    @Column(name = "STRT_DTE")
    private LocalDate startDate;

    @Column(name = "NAME")
    private String name;

    @Column(name = "ORDER")
    private Integer order;

    @Column(name = "END_DTE")
    private LocalDate endDate;
}

RoutePk:

@Data
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public class RouteEntityPk implements Serializable {

    private static final long serialVersionUID = 1L;

    private String routeId;
    private LocalDate startDate;
}



回答2:


This is one solution, probably not the best, but you can transform Pageable object in order to replace the field name like this :

In your controller getRouteByRouteId method :

List<Order> orders = pageable.getSort().stream().map(o -> o.getProperty().equals("startdate") ? new Order(o.getDirection(), "pk.startdate"): o).collect(Collectors.toList());

Then you can call the service with the modified object :

return ResponseEntity.ok(routeService.getRouteByRouteId(routeId, PageRequest.of(pageable.getPageNumber(),  pageable.getPageSize(), Sort.by(orders))));


来源:https://stackoverflow.com/questions/57771587/not-exposing-the-path-of-entities-that-have-a-composite-primary-key-to-the-front

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!