不幸的是,所有Spring Data JPA / Rest最高版本为2.1.0.RELEASE都无法立即满足你的需求。该源代码隐藏在Spring Data Commons / JPA本身中。Spring Data JPA仅支持
Id和EmbeddedId作为标识符。
摘录
JpaPersistentPropertyImpl:
static { // [...] annotations = new HashSet<Class<? extends Annotation>>(); annotations.add(Id.class); annotations.add(EmbeddedId.class); ID_ANNOTATIONS = annotations;}Spring Data Commons不支持组合属性的概念。它彼此独立地对待一个类的每个属性。
当然,你可以修改Spring Data Rest。但这很麻烦,不能从根本上解决问题,并且降低了框架的灵活性。
这是hack。这应该给你一个解决问题的思路。
在你的配置中覆盖
repositoryExporterHandlerAdapter并返回
CustomPersistentEntityResourceAssemblerArgumentResolver。此外,覆盖
backendIdConverterRegistry并添加
CustomBackendIdConverter到已知列表
id converter:
import org.springframework.beans.factory.ListableBeanFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.import;import org.springframework.data.rest.core.projection.ProxyProjectionFactory;import org.springframework.data.rest.webmvc.RepositoryRestHandlerAdapter;import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;import org.springframework.data.rest.webmvc.spi.BackendIdConverter;import org.springframework.data.rest.webmvc.support.HttpMethodHandlerMethodArgumentResolver;import org.springframework.data.web.config.EnableSpringDataWebSupport;import org.springframework.hateoas.ResourceProcessor;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.plugin.core.OrderAwarePluginRegistry;import org.springframework.plugin.core.PluginRegistry;import org.springframework.web.method.support.HandlerMethodArgumentResolver;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.List;@Configuration@import(RepositoryRestMvcConfiguration.class)@EnableSpringDataWebSupportpublic class RestConfig extends RepositoryRestMvcConfiguration { @Autowired(required = false) List<ResourceProcessor<?>> resourceProcessors = Collections.emptyList(); @Autowired ListableBeanFactory beanFactory; @Override @Bean public PluginRegistry<BackendIdConverter, Class<?>> backendIdConverterRegistry() { List<BackendIdConverter> converters = new ArrayList<BackendIdConverter>(3); converters.add(new CustomBackendIdConverter()); converters.add(BackendIdConverter.DefaultIdConverter.INSTANCE); return OrderAwarePluginRegistry.create(converters); } @Bean public RequestMappingHandlerAdapter repositoryExporterHandlerAdapter() { List<HttpMessageConverter<?>> messageConverters = defaultMessageConverters(); configureHttpMessageConverters(messageConverters); RepositoryRestHandlerAdapter handlerAdapter = new RepositoryRestHandlerAdapter(defaultMethodArgumentResolvers(), resourceProcessors); handlerAdapter.setMessageConverters(messageConverters); return handlerAdapter; } private List<HandlerMethodArgumentResolver> defaultMethodArgumentResolvers() { CustomPersistentEntityResourceAssemblerArgumentResolver peraResolver = new CustomPersistentEntityResourceAssemblerArgumentResolver( repositories(), entitylinks(), config().projectionConfiguration(), new ProxyProjectionFactory(beanFactory)); return Arrays.asList(pageableResolver(), sortResolver(), serverHttpRequestMethodArgumentResolver(), repoRequestArgumentResolver(), persistentEntityArgumentResolver(), resourcemetadataHandlerMethodArgumentResolver(), HttpMethodHandlerMethodArgumentResolver.INSTANCE, peraResolver, backendIdHandlerMethodArgumentResolver()); }}创建
CustomBackendIdConverter。此类负责呈现你的自定义实体ID:
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;import java.io.Serializable;public class CustomBackendIdConverter implements BackendIdConverter { @Override public Serializable fromRequestId(String id, Class<?> entityType) { return id; } @Override public String toRequestId(Serializable id, Class<?> entityType) { if(entityType.equals(Customer.class)) { Customer c = (Customer) id; return c.getId() + "_" +c.getStartVersion(); } return id.toString(); } @Override public boolean supports(Class<?> delimiter) { return true; }}CustomPersistentEntityResourceAssemblerArgumentResolver反过来应该返回一个
CustomPersistentEntityResourceAssembler:
import org.springframework.core.MethodParameter;import org.springframework.data.repository.support.Repositories;import org.springframework.data.rest.core.projection.ProjectionDefinitions;import org.springframework.data.rest.core.projection.ProjectionFactory;import org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler;import org.springframework.data.rest.webmvc.config.PersistentEntityResourceAssemblerArgumentResolver;import org.springframework.data.rest.webmvc.support.PersistentEntityProjector;import org.springframework.hateoas.Entitylinks;import org.springframework.web.bind.support.WebDataBinderFactory;import org.springframework.web.context.request.NativeWebRequest;import org.springframework.web.method.support.ModelAndViewContainer;public class CustomPersistentEntityResourceAssemblerArgumentResolver extends PersistentEntityResourceAssemblerArgumentResolver { private final Repositories repositories; private final Entitylinks entitylinks; private final ProjectionDefinitions projectionDefinitions; private final ProjectionFactory projectionFactory; public CustomPersistentEntityResourceAssemblerArgumentResolver(Repositories repositories, Entitylinks entitylinks, ProjectionDefinitions projectionDefinitions, ProjectionFactory projectionFactory) { super(repositories, entitylinks,projectionDefinitions,projectionFactory); this.repositories = repositories; this.entitylinks = entitylinks; this.projectionDefinitions = projectionDefinitions; this.projectionFactory = projectionFactory; } public boolean supportsParameter(MethodParameter parameter) { return PersistentEntityResourceAssembler.class.isAssignableFrom(parameter.getParameterType()); } public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { String projectionParameter = webRequest.getParameter(projectionDefinitions.getParameterName()); PersistentEntityProjector projector = new PersistentEntityProjector(projectionDefinitions, projectionFactory, projectionParameter); return new CustomPersistentEntityResourceAssembler(repositories, entitylinks, projector); }}CustomPersistentEntityResourceAssembler需要覆盖
getSelflinkFor。如你所见,e
ntity.getIdProperty()返回
Customer类的id或
startVersion属性,而该属性又被用于借助来检索实际值
BeanWrapper。在这里,我们使用
instanceof运算符将整个框架短路。因此,你的
Customer班级应实施
Serializable进一步处理。
import org.springframework.data.mapping.PersistentEntity;import org.springframework.data.mapping.model.BeanWrapper;import org.springframework.data.repository.support.Repositories;import org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler;import org.springframework.data.rest.webmvc.support.Projector;import org.springframework.hateoas.Entitylinks;import org.springframework.hateoas.link;import org.springframework.util.Assert;public class CustomPersistentEntityResourceAssembler extends PersistentEntityResourceAssembler { private final Repositories repositories; private final Entitylinks entitylinks; public CustomPersistentEntityResourceAssembler(Repositories repositories, Entitylinks entitylinks, Projector projector) { super(repositories, entitylinks, projector); this.repositories = repositories; this.entitylinks = entitylinks; } public link getSelflinkFor(Object instance) { Assert.notNull(instance, "Domain object must not be null!"); Class<? extends Object> instanceType = instance.getClass(); PersistentEntity<?, ?> entity = repositories.getPersistentEntity(instanceType); if (entity == null) { throw new IllegalArgumentException(String.format("Cannot create self link for %s! No persistent entity found!", instanceType)); } Object id; //this is a hack for demonstration purpose. don't do this at home! if(instance instanceof Customer) { id = instance; } else { BeanWrapper<Object> wrapper = BeanWrapper.create(instance, null); id = wrapper.getProperty(entity.getIdProperty()); } link resourcelink = entitylinks.linkToSingleResource(entity.getType(), id); return new link(resourcelink.getHref(), link.REL_SELF); }}而已!你应该看到以下URI:
{ "_embedded" : { "customers" : [ { "name" : "test", "_links" : { "self" : { "href" : "http://localhost:8080/demo/customers/1_1" } } } ] }}恕我直言,如果你正在从事绿色项目,我建议你
IdClass完全放弃并使用基于Long类的技术简单ID。这已通过Spring Data Rest 2.1.0.RELEASE,Spring data JPA 1.6.0.RELEASE和Spring framework 4.0.3RELEASE进行了测试。



