英文:
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 包下的 Customer
和 Order
类的定义。
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
{
"timestamp": "2020-09-01T17:34:28.779+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "",
"path": "/api/customers"
}
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='30000 ms'}
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 'applicationTaskExecutor'
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 ''
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 'dispatcherServlet'
2020-09-01 23:04:28.673 INFO 15824 --- [nio-8086-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
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'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'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("/api")
public class CustomerController {
@Autowired
private CustomerService customerService;
@Autowired
private OrderService orderService;
@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();
}
}
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<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;
}
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<String, Order> getOrders() {
return orders;
}
public void setOrders(final Map<String, Order> orders) {
this.orders = orders;
}
@Override
public String toString() {
return "Customer{" +
"customerId='" + customerId + '\'' +
", customerName='" + customerName + '\'' +
", companyName='" + companyName + '\'' +
", orders=" + orders +
'}';
}
}
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<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;
}
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 "Order{" +
"orderId='" + orderId + '\'' +
", price=" + price +
", quantity=" + quantity +
'}';
}
}
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<Customer, String> {
}
OrderRepository
package com.devzigma.repository;
import com.devzigma.model.Order;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface OrderRepository extends MongoRepository<Order, String> {
}
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<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.*;
@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();
}
}
then the OrderService with it's implementation
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.*;
@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);
}
}
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("10A", "Jane", "ABC Company");
final Customer customer2 = new Customer("20B", "Bob", "XYZ Company");
final Customer customer3 = new Customer("30C", "Tim", "CKV Company");
HashMap<String, Order> cus1order = new HashMap<>();
cus1order.put("001A", new Order("001A", 150.00, 25));
cus1order.put("002A", new Order("002A", 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
你确定你希望你的实体/模型,即 Customer
和 Order
,继承自具有属性 private final List<Link> links;
的 RepresentationModel
类吗?
如果你查看一下 RepresentationModel.java
的定义,它明确表示它是 用于收集链接的DTO的基类,所以我认为它应该用于DTO,而不是实体。
或者,如果你希望按照你所描述的方式进行,我唯一看到的避免错误的方法是忽略将 links
字段持久化:
- 覆盖
links
的 getter 方法 - 在
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<Link> 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 :
- Overriding the getter of
links
- Placing
@Transient
on the getter oflinks
property ofRepresentationModel
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<Link> 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<MyRepresentationModel> { ...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论