Spring HATEOAS RepresentationModel, Cannot set property links because no setter, no wither and it's not part of the persistence constructor

huangapple go评论81阅读模式
英文:

Spring HATEOAS RepresentationModel, Cannot set property links because no setter, no wither and it's not part of the persistence constructor

问题

CustomerController 类如下:

package com.devzigma.controller;

import com.devzigma.model.Customer;
import com.devzigma.services.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.Link;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Optional;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

@RestController
@RequestMapping("/api")
public class CustomerController {

    @Autowired
    private CustomerService customerService;

    @GetMapping(value = "/customers/{customerId}")
    public Optional<Customer> getCustomerById(@PathVariable String customerId) {
        return customerService.getCustomerDetail(customerId);
    }

    @GetMapping(value = "/customers")
    public List<Customer> getAll() {
        return customerService.allCustomers();
    }
}

以下是 model 包下的 CustomerOrder 类的定义。

Customer.java 类如下:

package com.devzigma.model;

import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.hateoas.RepresentationModel;

import java.util.Map;

@Document
public class Customer extends RepresentationModel<Customer> {

    private String customerId;
    private String customerName;
    private String companyName;
    private Map<String, Order> orders;

    public Customer() {
    }

    public Customer(String customerId, String customerName, String companyName) {
        this.customerId = customerId;
        this.customerName = customerName;
        this.companyName = companyName;
    }

    // Getter 和 Setter 方法...

    @Override
    public String toString() {
        return "Customer{" +
                "customerId='" + customerId + '\'' +
                ", customerName='" + customerName + '\'' +
                ", companyName='" + companyName + '\'' +
                ", orders=" + orders +
                '}';
    }
}

Order.java 类如下:

package com.devzigma.model;

import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.hateoas.RepresentationModel;

@Document
public class Order extends RepresentationModel<Order> {

    private String orderId;
    private double price;
    private int quantity;

    public Order() {
    }

    public Order(String orderId, double price, int quantity) {
        this.orderId = orderId;
        this.price = price;
        this.quantity = quantity;
    }

    // Getter 和 Setter 方法...

    @Override
    public String toString() {
        return "Order{" +
                "orderId='" + orderId + '\'' +
                ", price=" + price +
                ", quantity=" + quantity +
                '}';
    }
}

CustomerService 接口和实现如下:

package com.devzigma.services;

import com.devzigma.model.Customer;

import java.util.List;
import java.util.Optional;

public interface CustomerService {

    Optional<Customer> getCustomerDetail(String id);

    List<Customer> allCustomers();
}
package com.devzigma.services;

import com.devzigma.model.Customer;
import com.devzigma.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class CustomerServiceImpl implements CustomerService {

    @Autowired
    private CustomerRepository customerRepository;

    @Override
    public Optional<Customer> getCustomerDetail(String id) {
        return customerRepository.findById(id);
    }

    @Override
    public List<Customer> allCustomers() {
        return customerRepository.findAll();
    }
}

OrderService 接口和实现如下:

package com.devzigma.services;

import com.devzigma.model.Order;

import java.util.Optional;

public interface OrderService {

    Optional<Order> getAllOrdersForCustomer(String customerId);

}
package com.devzigma.services;

import com.devzigma.model.Order;
import com.devzigma.repository.CustomerRepository;
import com.devzigma.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private CustomerRepository customerRepository;

    @Autowired
    private OrderRepository orderRepository;

    @Override
    public Optional<Order> getAllOrdersForCustomer(String customerId) {
        return orderRepository.findById(customerId);
    }
}

这个代码段显示了您在 Spring Boot 项目中定义的一些重要组件,包括控制器、模型、服务和存储库。如果您有其他翻译需求,请随时提问。

英文:

GOAL: I'm just trying to invoke a get call on REST endpoint of a Spring HATEOAS application.
This is a simple project with MongoDB as the database.

Expected Result: When I try to invoke Get endpoint from the REST controller class, appropriate response should be return.

Actual Result: cannot invoke Get endpoint, giving me an internal server error when invoking using the postman.

below is the REST endpoint I'm trying to invoke

localhost:8086/api/customers

below is the error response

{
    &quot;timestamp&quot;: &quot;2020-09-01T17:34:28.779+00:00&quot;,
    &quot;status&quot;: 500,
    &quot;error&quot;: &quot;Internal Server Error&quot;,
    &quot;message&quot;: &quot;&quot;,
    &quot;path&quot;: &quot;/api/customers&quot;
}

What I've tried: Basically My Spring boot application trying to do the CRUD operations of Customer and order functionality.
initilaly I've preloaded some sample data to the mongodb database. When I ran the application I can see all the data from the MongoDB compass.
My app starts without any errors. But when I tried to invoke any REST endpoint then it gives me an error.

below is the complete stacktrace.

2020-09-01 23:03:11.446  INFO 15824 --- [           main] com.devzigma.DemoApplication             : Starting DemoApplication on SLL014289 with PID 15824 (D:\Java-excersie\spring-hateoas-baeldung - with mongo\target\classes started by buddhika_jayakodi in D:\Java-excersie\spring-hateoas-baeldung - with mongo)
2020-09-01 23:03:11.449  INFO 15824 --- [           main] com.devzigma.DemoApplication             : No active profile set, falling back to default profiles: default
2020-09-01 23:03:12.192  INFO 15824 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data MongoDB repositories in DEFAULT mode.
2020-09-01 23:03:12.288  INFO 15824 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 91ms. Found 2 MongoDB repository interfaces.
2020-09-01 23:03:12.994  INFO 15824 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8086 (http)
2020-09-01 23:03:13.005  INFO 15824 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-09-01 23:03:13.005  INFO 15824 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.37]
2020-09-01 23:03:13.177  INFO 15824 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-09-01 23:03:13.177  INFO 15824 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1650 ms
2020-09-01 23:03:13.320  INFO 15824 --- [           main] org.mongodb.driver.cluster               : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout=&#39;30000 ms&#39;}
2020-09-01 23:03:13.371  INFO 15824 --- [localhost:27017] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:1, serverValue:164}] to localhost:27017
2020-09-01 23:03:13.375  INFO 15824 --- [localhost:27017] org.mongodb.driver.cluster               : Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=9, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=2701575}
2020-09-01 23:03:14.086  INFO 15824 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService &#39;applicationTaskExecutor&#39;
2020-09-01 23:03:14.287  INFO 15824 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8086 (http) with context path &#39;&#39;
2020-09-01 23:03:14.298  INFO 15824 --- [           main] com.devzigma.DemoApplication             : Started DemoApplication in 3.483 seconds (JVM running for 4.443)
2020-09-01 23:03:14.338  INFO 15824 --- [           main] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:165}] to localhost:27017
2020-09-01 23:04:28.673  INFO 15824 --- [nio-8086-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet &#39;dispatcherServlet&#39;
2020-09-01 23:04:28.673  INFO 15824 --- [nio-8086-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet &#39;dispatcherServlet&#39;
2020-09-01 23:04:28.677  INFO 15824 --- [nio-8086-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 4 ms
2020-09-01 23:04:28.754 ERROR 15824 --- [nio-8086-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: Cannot set property links because no setter, no wither and it&#39;s not part of the persistence constructor public com.devzigma.model.Order()!] with root cause
java.lang.IllegalStateException: Cannot set property links because no setter, no wither and it&#39;s not part of the persistence constructor public com.devzigma.model.Order()!
at org.springframework.data.mapping.model.InstantiationAwarePropertyAccessor.setProperty(InstantiationAwarePropertyAccessor.java:118) ~[spring-data-commons-2.3.3.RELEASE.jar:2.3.3.RELEASE]
at org.springframework.data.mapping.model.ConvertingPropertyAccessor.setProperty(ConvertingPropertyAccessor.java:63) ~[spring-data-commons-2.3.3.RELEASE.jar:2.3.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readProperties(MappingMongoConverter.java:450) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.populateProperties(MappingMongoConverter.java:367) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:347) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:317) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readMap(MappingMongoConverter.java:1187) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:288) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1580) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1478) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readProperties(MappingMongoConverter.java:450) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.populateProperties(MappingMongoConverter.java:367) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:347) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:317) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:250) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:246) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:98) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.MongoTemplate$ReadDocumentCallback.doWith(MongoTemplate.java:3141) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:2788) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:2518) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:2500) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:856) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findAll(SimpleMongoRepository.java:383) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findAll(SimpleMongoRepository.java:205) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findAll(SimpleMongoRepository.java:55) ~[spring-data-mongodb-3.0.3.RELEASE.jar:3.0.3.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_221]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_221]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_221]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_221]
at org.springframework.data.repository.core.support.ImplementationInvocationMetadata.invoke(ImplementationInvocationMetadata.java:72) ~[spring-data-commons-2.3.3.RELEASE.jar:2.3.3.RELEASE]
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:382) ~[spring-data-commons-2.3.3.RELEASE.jar:2.3.3.RELEASE]
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:205) ~[spring-data-commons-2.3.3.RELEASE.jar:2.3.3.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:549) ~[spring-data-commons-2.3.3.RELEASE.jar:2.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:155) ~[spring-data-commons-2.3.3.RELEASE.jar:2.3.3.RELEASE]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:130) ~[spring-data-commons-2.3.3.RELEASE.jar:2.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80) ~[spring-data-commons-2.3.3.RELEASE.jar:2.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at com.sun.proxy.$Proxy60.findAll(Unknown Source) ~[na:na]
at com.devzigma.services.CustomerServiceImpl.allCustomers(CustomerServiceImpl.java:24) ~[classes/:na]
at com.devzigma.controller.CustomerController.getAll(CustomerController.java:38) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_221]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_221]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_221]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_221]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:626) ~[tomcat-embed-core-9.0.37.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.37.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.37.jar:9.0.37]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_221]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_221]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.37.jar:9.0.37]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_221]

below is the CustomerController class.

package com.devzigma.controller;
import com.devzigma.model.Customer;
import com.devzigma.model.Order;
import com.devzigma.services.CustomerService;
import com.devzigma.services.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.Link;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Optional;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
@RestController
@RequestMapping(&quot;/api&quot;)
public class CustomerController {
@Autowired
private CustomerService customerService;
@Autowired
private OrderService orderService;
@GetMapping(value = &quot;/customers/{customerId}&quot;)
public Optional&lt;Customer&gt; getCustomerById(@PathVariable String customerId) {
return customerService.getCustomerDetail(customerId);
}
@GetMapping(value = &quot;/customers&quot;)
public List&lt;Customer&gt; getAll() {
return customerService.allCustomers();
}
}

below are the Customer and Order classes of model package

Customer.java class

package com.devzigma.model;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.hateoas.RepresentationModel;
import java.util.Map;
@Document
public class Customer extends RepresentationModel&lt;Customer&gt; {
private String customerId;
private String customerName;
private String companyName;
private Map&lt;String, Order&gt; orders;
public Customer() {
}
public Customer(String customerId, String customerName, String companyName) {
this.customerId = customerId;
this.customerName = customerName;
this.companyName = companyName;
}
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Map&lt;String, Order&gt; getOrders() {
return orders;
}
public void setOrders(final Map&lt;String, Order&gt; orders) {
this.orders = orders;
}
@Override
public String toString() {
return &quot;Customer{&quot; +
&quot;customerId=&#39;&quot; + customerId + &#39;\&#39;&#39; +
&quot;, customerName=&#39;&quot; + customerName + &#39;\&#39;&#39; +
&quot;, companyName=&#39;&quot; + companyName + &#39;\&#39;&#39; +
&quot;, orders=&quot; + orders +
&#39;}&#39;;
}
}

then the Order.java class

package com.devzigma.model;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.hateoas.RepresentationModel;
@Document
public class Order extends RepresentationModel&lt;Order&gt; {
private String orderId;
private double price;
private int quantity;
public Order() {
}
public Order(String orderId, double price, int quantity) {
this.orderId = orderId;
this.price = price;
this.quantity = quantity;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
@Override
public String toString() {
return &quot;Order{&quot; +
&quot;orderId=&#39;&quot; + orderId + &#39;\&#39;&#39; +
&quot;, price=&quot; + price +
&quot;, quantity=&quot; + quantity +
&#39;}&#39;;
}
}

below are the two repository interfaces which extends MongoRepository<>

CustomerRepository

package com.devzigma.repository;
import com.devzigma.model.Customer;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface CustomerRepository extends MongoRepository&lt;Customer, String&gt; {
}

OrderRepository

package com.devzigma.repository;
import com.devzigma.model.Order;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface OrderRepository extends MongoRepository&lt;Order, String&gt; {
}

In the services package I have CustomerService and OrderService interfaces with their respective implementations.

CustomerService and it's implementation

package com.devzigma.services;
import com.devzigma.model.Customer;
import java.util.List;
import java.util.Optional;
public interface CustomerService {
Optional&lt;Customer&gt; getCustomerDetail(String id);
List&lt;Customer&gt; allCustomers();
}
package com.devzigma.services;
import com.devzigma.model.Customer;
import com.devzigma.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class CustomerServiceImpl implements CustomerService {
@Autowired
private CustomerRepository customerRepository;
@Override
public Optional&lt;Customer&gt; getCustomerDetail(String id) {
return customerRepository.findById(id);
}
@Override
public List&lt;Customer&gt; allCustomers() {
return customerRepository.findAll();
}
}

then the OrderService with it's implementation

package com.devzigma.services;
import com.devzigma.model.Order;
import java.util.Optional;
public interface OrderService {
Optional&lt;Order&gt; getAllOrdersForCustomer(String customerId);
}
package com.devzigma.services;
import com.devzigma.model.Order;
import com.devzigma.repository.CustomerRepository;
import com.devzigma.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private OrderRepository orderRepository;
@Override
public Optional&lt;Order&gt; getAllOrdersForCustomer(String customerId) {
return orderRepository.findById(customerId);
}
}

In my main class I have implements the CommandLineRunner so that I can populate MongoDB database with sample data.

below is the main class.

package com.devzigma;
import com.devzigma.model.Customer;
import com.devzigma.model.Order;
import com.devzigma.repository.CustomerRepository;
import com.devzigma.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.HashMap;
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private OrderRepository orderRepository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
customerRepository.deleteAll();
orderRepository.deleteAll();
final Customer customer1 = new Customer(&quot;10A&quot;, &quot;Jane&quot;, &quot;ABC Company&quot;);
final Customer customer2 = new Customer(&quot;20B&quot;, &quot;Bob&quot;, &quot;XYZ Company&quot;);
final Customer customer3 = new Customer(&quot;30C&quot;, &quot;Tim&quot;, &quot;CKV Company&quot;);
HashMap&lt;String, Order&gt; cus1order = new HashMap&lt;&gt;();
cus1order.put(&quot;001A&quot;, new Order(&quot;001A&quot;, 150.00, 25));
cus1order.put(&quot;002A&quot;, new Order(&quot;002A&quot;, 250.00, 15));
customer1.setOrders(cus1order);
customerRepository.save(customer1);
customerRepository.save(customer2);
customerRepository.save(customer3);
}
}

finally , below is my application.properties file

server.port=8086
spring.data.mongodb.authentication-database=admin
spring.data.mongodb.uri=mongodb://localhost:27017/customer-order

I know this is lot of boilerplate code. but please bear with me.
If can anyone give my insight how to fix above error which was given as the stacktrace, then it would be much appreciated.

Good day!

答案1

得分: 3

你确定你希望你的实体/模型,即 CustomerOrder,继承自具有属性 private final List&lt;Link&gt; links;RepresentationModel 类吗?

如果你查看一下 RepresentationModel.java定义,它明确表示它是 用于收集链接的DTO的基类所以我认为它应该用于DTO,而不是实体

或者,如果你希望按照你所描述的方式进行,我唯一看到的避免错误的方法是忽略将 links 字段持久化:

  1. 覆盖 links 的 getter 方法
  2. RepresentationModel 类的 links 属性的 getter 方法上加上 @Transient

代码如下所示:

@Transient
@Override
public Links getLinks() {
    return super.getLinks();
}

如果这种方法不起作用,试试为你的实体创建专用的DTO,每个DTO都继承自 RepresentationModel,这样你就可以避免杂乱的异常,保持代码整洁!

英文:

Are you sure that you want your entities/models; Customer and Order, to inherit from RepresentationModel class which have the property private final List&lt;Link&gt; links; ?

If you have a look at the definition of RepresentationModel.java it says clearly that it's a Base class for DTOs to collect links., so IMHO I think that it should be used for DTOs not for entities.

Otherwise if you want to keep it as you described, the only way that I see to avoid the error is by ignoring the links field from being persisted :

  1. Overriding the getter of links
  2. Placing @Transienton the getter of links property of RepresentationModel class

It will look like this :

@Transient
@Override
public Links getLinks() {
return super.getLinks();
}

If this approach doesn't work, try creating dedicated DTOs for your entities, and each of your DTOs will inherit from RepresentationModel, this way you'll avoid the noisy exception and keep things clean !

答案2

得分: 0

一种可能的解决方法是通过setter使属性变为可变(假设持久化空链接集合是可以接受的):

@AccessType(AccessType.Type.PROPERTY)
public void setLinks(List<Link> links) {
    super.removeLinks();
    super.add(links);
}

你甚至可以将此方法放在自定义的父类中,并使该类扩展RepresentationModel,例如:

public abstract class MyRepresentationModel extends RepresentationModel<MyRepresentationModel> {
    // ...
}
英文:

One possible way to resolve this exception is to make the property mutable via a setter (assuming it's acceptable to persist an empty links collection):

@AccessType(AccessType.Type.PROPERTY)
public void setLinks(List&lt;Link&gt; links) {
super.removeLinks();
super.add(links);
}

You can even place this in a custom parent class and have that class extend RepresentationModel, e.g.:

public abstract class MyRepresentationModel extends RepresentationModel&lt;MyRepresentationModel&gt; { ...

huangapple
  • 本文由 发表于 2020年9月2日 02:40:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/63693623.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定