1

I have a controller that looks like that

@RestController
public class LocationsController {

    @Autowired
    private EntityManager manager;

    private String withinQuery =
            "WITH L as\n" +
            "\n" +
            "(SELECT *\n" +
            "FROM location\n" +
            "\n" +
            "WHERE ST_Distance(ST_FlipCoordinates(location.shape), ST_FlipCoordinates(ST_GeomFromGeoJSON('%s'\n" +
            "        )))=0)\n" +
            "\n" +
            "SELECT *\n" +
            "FROM L\n" +
            "WHERE id NOT IN (\n" +
            "SELECT metalocation_id FROM location\n" +
            "WHERE metalocation_id IS NOT NULL\n" +
            ")";

    private String nearestQuery =
            "select * from location order by ST_Distance(ST_FlipCoordinates(location.shape), ST_FlipCoordinates(St_GeomFromGeoJSON('%s'))) limit 1";

    @RequestMapping(value="near", method = RequestMethod.GET)
    public List<Location> getNearestLocations(@RequestParam(value = "point") String pointAsString) throws IOException {
        List<Location> locationCloseToPoint = manager.createNativeQuery(String.format(withinQuery, pointAsString), Location.class).getResultList();
        if (locationCloseToPoint.size() == 0) {
            List<Location> closesLocation = manager.createNativeQuery(String.format(nearestQuery, pointAsString), Location.class)
                    .getResultList();
            locationCloseToPoint.addAll(closesLocation);
        }
        return locationCloseToPoint;
    }
}

As you can see it return list of locations.

@Entity
public class Location {

    public Geometry getShape() {
        return shape;
    }

    public void setShape(Geometry shape) {
        this.shape = shape;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @NotNull
    private Geometry shape;

    @ManyToOne(cascade = CascadeType.ALL)
    private Location metalocation;

The problem with that is I want to return this list in format that spring data rest uses for location resource with all hateoas fields and stuff. More specifically I want to have a link to metalocation in the output.

I've read about spring-hateoas and ResourceAssembler and @RepositoryRestController and I think I could replicate what spring-data-rest is doing via writing custom ResourceAssembler, but I don't want to, because you know, why would I want to write the code that is already written by spring-data-rest, right?

They doing all this assembling stuff automatically, right? Because I see it in the http output. So I think there should be a way to use it.

1 Answer 1

1

You can try the following.

First annotate your controller with @RepositoryRestController instead of @RestController.

You can then use the resource assembler that spring data rest uses internally - PersistentEntityResourceAssembler

The example below works for me.

@RepositoryRestController
public class DemoController {

    private final ProductRepository productRepository;
    private final PagedResourcesAssembler<Object> pagedResourcesAssembler;

    @Autowired
    public DemoController(ProductRepository productRepository,
                          PagedResourcesAssembler<Object> pagedResourcesAssembler) {
        this.productRepository = productRepository;
        this.pagedResourcesAssembler = pagedResourcesAssembler;
    }

    //use PersistentEntityResourceAssembler and PagedResourcesAssembler to return a resource with paging links
    @RequestMapping(method = GET, path="/products/search/listProductsPage", produces = HAL_JSON_VALUE)
    public ResponseEntity<PagedResources<PersistentEntityResource>> getAllPage(Pageable pageable, PersistentEntityResourceAssembler persistentEntityResourceAssembler) {
        Iterable<?> all = productRepository.findAll(pageable);

        return ResponseEntity.ok(pagedResourcesAssembler.toResource((Page<Object>) all, persistentEntityResourceAssembler));
    }

    //return Resources of entity resources
    @RequestMapping(method = GET, path="/products/search/listProducts", produces = HAL_JSON_VALUE)
    public ResponseEntity<Resources<PersistentEntityResource>> getAll(Pageable pageable, PersistentEntityResourceAssembler persistentEntityResourceAssembler) {

        return ResponseEntity.ok(new Resources<PersistentEntityResource>(productRepository.findAll().stream()
                .map(persistentEntityResourceAssembler::toResource)
                .collect(Collectors.toList())));
    }
}

The getAll method is probably what you want.

I added also the getAllPage variant that converts a Page into a PagedResource - this is what spring data rest generates if you get a collection resource. Here you also have to use PagedResourcesAssembler to generate the page links.

Is this what you are searching for?

In your case I would also try to avoid the custom controller - If you could express your native query as a repository finder, spring data rest would automatically expose the finder. Spring data jpa seems to support native queries via annotation - http://docs.spring.io/spring-data/jpa/docs/1.9.1.RELEASE/reference/html/#jpa.query-methods.at-query

Sign up to request clarification or add additional context in comments.

5 Comments

Just saw that you are not using the Page at all . so in your case it would be enough to go over your list and use PersistentEntityResourceAssembler to convert each item in your list.
Thanks, I was able to find that I can use PersistentEntityResourceAssembler and tried to return List<PersistentEntity> but that gaved me not found error despite the list of persistent entities was correct. So, I would appreciate if you also would give some links to the documentation where I can read why it's actually working. Thanks!
What I was trying to show in my example code is that you should not return a List of your entity. You need to return a Resource - Resources<PersistentEntityResource>. The use of PersistentEntityResourceAssembler is not documented as far as I could see.
Just edited my answer - please see the last paragraph for a suggestion on how to change your solution idea.
While I could use @Query annotation for both of the queries, as you can see I still need a controller, because there is a logic there. If the first query returns empty, then and only then I need the second query. I can express then with postgres stored procedure, but I don't think that this is the way to go.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.