我在上配置了RepositoryRestResource
,PageAndSortingRepository
用于访问包含复合ID的实体:
@Entity
@IdClass(CustomerId.class)
public class Customer {
@Id BigInteger id;
@Id int startVersion;
...
}
public class CustomerId {
BigInteger id;
int startVersion;
...
}
@RepositoryRestResource(collectionResourceRel = "customers", path = "customers", itemResourceRel = "customers/{id}_{startVersion}")
public interface CustomerRepository extends PagingAndSortingRepository<Customer, CustomerId> {}
"http://<server>/api/customers/1_1"
例如,当我访问服务器时,我以json的形式返回了正确的资源,但是_links部分中针对self的href是错误的,并且对于我查询的任何其他客户也是相同的:"http://<server>/api/customer/1"
即:
{
"id" : 1,
"startVersion" : 1,
...
"firstname" : "BOB",
"_links" : {
"self" : {
"href" : "http://localhost:9081/reps/api/reps/1" <-- This should be /1_1
}
}
}
我想这是因为我的复合ID,但是我对如何更改此默认行为感到烦恼。
我看过ResourceSupport
和ResourceProcessor
类,但不确定是否需要更改多少才能解决此问题。
不幸的是,所有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)
@EnableSpringDataWebSupport
public 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
。如你所见,entity.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进行了测试。
我有一个JPA实体的类层次结构,基类是定义了一个ID的MappedSuperclass。我试图在子类中使用复合键,但这似乎不起作用
问题内容: 这是我的http服务器: 我需要在myHander内部访问实例t1。 有什么办法吗? 谢谢! 问题答案: 有一种方法可以将属性设置为class: 您必须注意,在所有使用myHandler的地方都将是t1的相同实例
考虑到Spring数据存储库的自定义实现,我使用存储库上的带来所有HATEOAS生成的Goodnes: 现在,按照前面提到的文档,我已经用附加的简单方法创建了,用于介绍目的: 实施方式是: 我希望默认的SDR自动生成endpoint保持,只添加新的自定义方法/新的实现。我应该如何将这个自定义方法与spring-data REST/HATEOAS一起使用?使用简单的可以自动生成控制器endpoint
问题内容: 我正在使用Spring-data- rest在某些JPA实体上提供读取的API。对于写操作,我需要发出Command对象,而不是直接写到DB,因此我添加了一个使用各种命令处理方法的自定义控制器: 我希望输出像spring-data-rest控制器的任何其他响应一样,得到充实,特别是我希望它为自身及其关系添加HATEOAS链接。 问题答案: 最近,Oliver Gierke亲自回答了这个
我在Node.js忘记密码后端路由,我试图使用nodemailer从我从namecheap.com购买的自定义域发送电子邮件,以及电子邮件域。我不确定这是主机、端口/安全还是授权的问题。但是,当我更改主机时,它会给出一个ECONREFUSED错误,所以我相信该部分正在工作。我的防火墙被禁用了(据我所知),我重新启动了,但是很难判断,因为诺顿防病毒软件控制着它。 这是我的代码,取自路由器。在我的后端
我在使用聚合框架从MongoDB读取文档时遇到了问题:我的结果中总是得到空ID。这只发生在具有复合ID的文档中。我尝试了各种版本的spring-data-mongob(1.10.12, 2.0.7),结果相同。 实体定义类 测试代码 输出 调试到以下方法MappingMongoConverter。read(final mongopersistenentity entity、final Docume