You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取@RepositoryRestResource生成的嵌套REST数据及优化Traverson调用

Hey there! Since you're new to HAL and HATEOAS, let's walk through your two requirements with clear, actionable steps:

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:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 19:32:37