Spring Boot中使用JPA的InheritanceType.JOINED时合并子实体到单个_embedded数组
解决Spring Data Rest返回继承实体拆分数组的问题
我懂你的困扰——当用Spring Data Rest暴露父类Repository时,默认会把不同子类的实体拆分成各自的嵌入式数组返回,确实不符合你想要的统一数组需求。针对Spring Boot 1.5.10这个版本,我给你两个实用的解决方案:
方案一:自定义ResourceProcessor合并资源
这是最直接的方法,通过实现Spring Data Rest的ResourceProcessor接口,在返回响应前手动合并不同子类的资源数组。
- 创建一个处理器类,让Spring自动扫描并加载它:
import org.springframework.data.rest.webmvc.ResourceProcessor; import org.springframework.hateoas.Resources; import org.springframework.hateoas.Resource; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @Component public class MergedMessageResourceProcessor implements ResourceProcessor<Resources<Resource<Message>>> { @Override public Resources<Resource<Message>> process(Resources<Resource<Message>> resources) { // 收集所有子类的消息实例 List<Resource<Message>> allMessages = new ArrayList<>(); Map<String, List<Resource<Message>>> originalEmbedded = resources.getEmbedded(); // 把AMessage和BMessage的资源都加到统一列表里 if (originalEmbedded.containsKey("aMessages")) { allMessages.addAll(originalEmbedded.get("aMessages")); } if (originalEmbedded.containsKey("bMessages")) { allMessages.addAll(originalEmbedded.get("bMessages")); } // 构建新的嵌入式结构,只保留"messages"数组 Map<String, List<Resource<Message>>> mergedEmbedded = new LinkedHashMap<>(); mergedEmbedded.put("messages", allMessages); // 返回合并后的资源,保留原有的链接信息 return new Resources<>(allMessages, mergedEmbedded, resources.getLinks()); } }
- 如果你的接口支持分页,还需要额外实现针对分页资源的处理器:
import org.springframework.data.rest.webmvc.ResourceProcessor; import org.springframework.hateoas.PagedResources; import org.springframework.hateoas.Resource; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @Component public class MergedPagedMessageResourceProcessor implements ResourceProcessor<PagedResources<Resource<Message>>> { @Override public PagedResources<Resource<Message>> process(PagedResources<Resource<Message>> resources) { List<Resource<Message>> allMessages = new ArrayList<>(); Map<String, List<Resource<Message>>> originalEmbedded = resources.getEmbedded(); if (originalEmbedded.containsKey("aMessages")) { allMessages.addAll(originalEmbedded.get("aMessages")); } if (originalEmbedded.containsKey("bMessages")) { allMessages.addAll(originalEmbedded.get("bMessages")); } Map<String, List<Resource<Message>>> mergedEmbedded = new LinkedHashMap<>(); mergedEmbedded.put("messages", allMessages); return new PagedResources<>(allMessages, resources.getMetadata(), mergedEmbedded, resources.getLinks()); } }
这个方案的好处是不需要修改你的实体类或Repository定义,完全通过后置处理调整响应结构,兼容性很强。
方案二:调整实体与Rest配置(可选)
如果希望从源头控制,你可以在子类上标记@RestResource(exported = false),阻止Spring Data Rest单独暴露子类的端点,同时确保父类Repository能正确查询所有子类实例:
- 在AMessage和BMessage类上添加注解:
@Entity @RestResource(exported = false) public class AMessage extends Message { // 你的子类字段和方法 }
- 然后创建RepositoryRest配置类,确保父类实体被正确识别:
import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter; import org.springframework.stereotype.Component; @Component public class RestConfig extends RepositoryRestConfigurerAdapter { @Override public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { // 按需暴露父类实体的ID config.exposeIdsFor(Message.class); // 确保父类作为主要域类型处理 config.setReturnBodyOnCreate(true); config.setReturnBodyOnUpdate(true); } }
不过这个方案可能需要结合Jackson的类型信息注解(比如在Message类上添加@JsonTypeInfo和@JsonSubTypes),确保序列化时能正确识别子类类型,但最终是否能完全合并嵌入式数组,还是要看Spring Data Rest的版本行为,所以方案一的可靠性更高。
内容的提问来源于stack exchange,提问作者Ali
相关产品推荐
相关产品推荐

