0

I'm having an issue with the HAL output that I get when combining Spring Data Web Support, Pageables, Projections, and HATEOAS.

I have a JPA entity, with an interface projection. I have a JPA repository with a findAllProjectedBy(Pageable).

public interface FeatureRepository extends JpaRepository<Feature, Long> {
  Page<FeatureDataLoad> findAllProjectedBy(Pageable pageable);
}

I have a REST controller like this.

@GetMapping("/features")
public ResponseEntity<PagedResources<Resource<FeatureDataLoad>>> exportFeatures(
      Pageable pageable,
      PagedResourcesAssembler<FeatureDataLoad> assembler) {
  Page<FeatureDataLoad> page = featureRepository.findAllProjectedBy(pageable);

  PagedResources<Resource<FeatureDataLoad>> resource = assembler.toResource(page);
  return new ResponseEntity<PagedResources<Resource<FeatureDataLoad>>>(resource, HttpStatus.OK);
}

This works well and is surprisingly easily, but when combining all of these features, I get something odd in the HAL rendering.

{
  "_embedded": {
    "tupleBackedMapList": [
      {
        "property": "value" /* etc. */
      }
    ]
  },
  "_links": { /* standard links */ },
  "page": { /* standard pagination info */ }
}

Pretty amazing, given the amount of extra work I had to do to get HAL and REST support for my existing entities. But, what's up with "tupleBackedMapList"? Without the projection, I get what I expected, "featureList".

I can't find how I would either fix this, or "customize" the generation of that part.

I seems minor, but I'm trying to "sell" the organization of adopting HAL and I'm this seems too weird.

I'm not wanting to drop the projection because the Feature entity has a computed column that I need for the rest of the app, but it almost doubles the query time.

For what it's worth, I'm on Spring Boot 2.1.6.RELEASE.

Edit

After some experimentation, found that if I make the entity class implement the projection interface, I get what I want. The documentation for Spring Data doesn't say you should put the interface on the entity, but I'm OK with that. (It looks OK if I implement the interface, but it ignores the projection.)

Also, for what it's worth, I tried using @Relation to name the collection and that didn't help, but it did make the property name change. Oddly enough, if I use the @Relation annotation, it turns the property name to content which is what documentation says it should be.

Maybe I'll make a feature request to honor HATEOAS metadata annotations.

Edit 2

I worked around this by putting the common attributes in a @MappedSuperclass that I called FeatureBase and then made a Feature that extended that super class and added the expensive column computation and made a FeatureDataLoad that just extended the base. It was annoying, so I'd still like a real solution because projections are too easy otherwise.

And again, if this was the third or fourth service in HAL, I'd probably not worry too much about it, but since this is the first and I'm trying to drive adoption, I felt that this was important enough to spend the time on.

Jeff Walker
  • 1,656
  • 1
  • 18
  • 36

1 Answers1

0

FeatureDataLoad is not an entity, so PagedResourceAssembler cannot create links for it. (And cannot find out the name for it).

But if FeatureDataLoad is a projection for Feature, then - in theory - you can use PagedResourcesAssembler<Feature> assembler in your method. (Instead of PagedResourcesAssembler<FeatureDataLoad>)

Selindek
  • 3,269
  • 1
  • 18
  • 25