如何获取@RepositoryRestResource生成的嵌套REST数据及优化Traverson调用
Hey there! Since you're new to HAL and HATEOAS, let's walk through your two requirements with clear, actionable steps:
1. Get Category (or its link) in a single Traverson request without adding new @JsonProperty annotations
Spring Data REST automatically handles associations by either exposing them as links (in the _links section) or allowing you to embed them using the expand parameter—no need to add extra @JsonProperty to your entities. Here are two approaches:
Option A: Fetch the Category link
Spring Data REST adds a category link under each Joke's _links by default. To capture this in your JokesDTO, just add a field to map the link URL:
@Builder(toBuilder = true) @Value @JsonDeserialize(builder = JokesDTO.JokesDTOBuilder.class) @JsonInclude(Include.NON_NULL) public class JokesDTO { private String content; // Map the category link from HAL's _links section @JsonProperty("_links.category.href") private String categoryLink; @JsonPOJOBuilder(withPrefix = "") @JsonIgnoreProperties(ignoreUnknown = true) public static class JokesDTOBuilder { } }
Your existing Traverson code works as-is—no changes needed. The categoryLink will now hold the URL to fetch the full Category details later if you need it.
Option B: Embed full Category data in one request
Use Spring Data REST's expand query parameter to embed the Category directly in the Joke response. First, create a CategoryDTO to map the Category data:
@Builder(toBuilder = true) @Value @JsonDeserialize(builder = CategoryDTO.CategoryDTOBuilder.class) @JsonInclude(Include.NON_NULL) public class CategoryDTO { private int id; private String name; @JsonPOJOBuilder(withPrefix = "") @JsonIgnoreProperties(ignoreUnknown = true) public static class CategoryDTOBuilder { } }
Update your JokesDTO to include the embedded Category:
@Builder(toBuilder = true) @Value @JsonDeserialize(builder = JokesDTO.JokesDTOBuilder.class) @JsonInclude(Include.NON_NULL) public class JokesDTO { private String content; // Map the embedded Category from HAL's _embedded section @JsonProperty("_embedded.category") private CategoryDTO category; @JsonPOJOBuilder(withPrefix = "") @JsonIgnoreProperties(ignoreUnknown = true) public static class JokesDTOBuilder { } }
Modify your Traverson call to include the expand parameter:
Traverson client = new Traverson(URI.create("http://localhost:8080/api/"), MediaTypes.HAL_JSON); HashMap<String, Object> parameters = Maps.newHashMap(); parameters.put("size", "2"); parameters.put("expand", "category"); // Add this to embed Category PagedModel<JokesDTO> jokes = client .follow("jokes") .withTemplateParameters(parameters) .toObject(new PagedModelType<JokesDTO>() { }); return jokes;
This will return the full Category data alongside each Joke in one request.
2. Expose a custom query via @RepositoryRestResource to fetch all associated data in one go
You can use Spring Data REST's repository query methods with fetch joins or projections to grab Jokes and their Categories in a single database call. Here are two easy ways:
Option A: Custom repository query with fetch join
Create a custom query in your JokeRepository using JOIN FETCH to load Categories alongside Jokes:
@RepositoryRestResource(collectionResourceRel = "jokes", path = "jokes") public interface JokeRepository extends JpaRepository<Joke, Long> { // Fetch Jokes and their associated Categories in one query @Query("SELECT j FROM Joke j JOIN FETCH j.category") Page<Joke> findAllWithCategory(Pageable pageable); }
Spring Data REST automatically exposes this as a search endpoint at /api/jokes/search/findAllWithCategory. Update your Traverson call to follow this search link:
Traverson client = new Traverson(URI.create("http://localhost:8080/api/"), MediaTypes.HAL_JSON); HashMap<String, Object> parameters = Maps.newHashMap(); parameters.put("size", "2"); PagedModel<JokesDTO> jokes = client .follow("jokes", "search", "findAllWithCategory") // Navigate to the custom query .withTemplateParameters(parameters) .toObject(new PagedModelType<JokesDTO>() { });
Update your JokesDTO to include the CategoryDTO field directly (no _embedded prefix needed here, since the fetch join loads the data upfront):
@Builder(toBuilder = true) @Value @JsonDeserialize(builder = JokesDTO.JokesDTOBuilder.class) @JsonInclude(Include.NON_NULL) public class JokesDTO { private String content; private CategoryDTO category; @JsonPOJOBuilder(withPrefix = "") @JsonIgnoreProperties(ignoreUnknown = true) public static class JokesDTOBuilder { } }
Option B: Use a Spring Data REST Projection
Projections are a simpler way to shape the response without writing custom queries. Create a projection interface for Jokes that includes the Category:
@Projection(name = "withCategory", types = Joke.class) public interface JokeWithCategoryProjection { String getContent(); Category getCategory(); // Expose the full Category entity }
Then, use the projection parameter in your Traverson call to activate this projection:
Traverson client = new Traverson(URI.create("http://localhost:8080/api/"), MediaTypes.HAL_JSON); HashMap<String, Object> parameters = Maps.newHashMap(); parameters.put("size", "2"); parameters.put("projection", "withCategory"); // Use the projection PagedModel<JokesDTO> jokes = client .follow("jokes") .withTemplateParameters(parameters) .toObject(new PagedModelType<JokesDTO>() { });
This will return Jokes with their full Category data in one request, and you don't need to modify your repository interface beyond the default setup.
内容的提问来源于stack exchange,提问作者kingkong

