Angular + Spring boot REST, не может опубликовать объект с внешним ключом

Я создал простую примочку с загрузкой Angular + Spring. Я не мог найти решение своей проблемы. Я не могу опубликовать объект с внешним ключом. У меня есть две сущности: продукт и категория.

Category.java:

@Entity
@Table(name = "category")
public class ProductCategory {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "category_name")
    private String categoryName;

    @JsonManagedReference
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "category")
    private Set<Product> products;

    public ProductCategory() {
    }

    public ProductCategory (Long id) {
        super();
        this.id = id;
    }


    public Set<Product> getProducts() {
        return products;
    }

    public void setProducts(Set<Product> products) {
        this.products = products;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }
}

Product.java:

@Entity
@Table(name = "product")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "brand")
    private String brand;

    @Column(name = "name")
    private String name;

    @JsonBackReference
    @ManyToOne
    @JoinColumn(name = "category_id", nullable = false)
    private ProductCategory category;


    public Product(Long id, String brand, String name, ProductCategory category) {
        this.id = id;
        this.brand = brand;
        this.name = name;
        this.category = category;
    }

    public Product(){}

    public ProductCategory getCategory() {
        return category;
    }

    public void setCategory(ProductCategory category) {
        this.category = category;
    }

    public Long getCategoryId() {
        return category.getId();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Когда я сохраняю объект через почтальона, он отлично работает:

{
            "brand": "hp",
            "name": "envy",
            "category": {
                "id": 2
            }
        }

Но когда я публикую его в Angular, я получаю сообщение об ошибке в Spring / Вот трассировка стека:

 org.hibernate.PropertyValueException: not-null property references a null or transient value : com.codejava.shoptest.entity.Product.category
    at org.hibernate.engine.internal.Nullability.checkNullability(Nullability.java:111) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.engine.internal.Nullability.checkNullability(Nullability.java:55) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.action.internal.AbstractEntityInsertAction.nullifyTransientReferencesIfNotAlready(AbstractEntityInsertAction.java:116) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:72) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:645) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:282) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:263) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:317) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:330) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:287) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:123) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:185) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:128) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:721) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:707) ~[hibernate-core-5.4.20.Final.jar:5.4.20.Final]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:366) ~[spring-orm-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at com.sun.proxy.$Proxy93.persist(Unknown Source) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:314) ~[spring-orm-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at com.sun.proxy.$Proxy93.persist(Unknown Source) ~[na:na]
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:554) ~[spring-data-jpa-2.3.3.RELEASE.jar:2.3.3.RELEASE]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    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.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367) ~[spring-tx-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-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.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-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.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:178) ~[spring-data-jpa-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.$Proxy98.save(Unknown Source) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at com.sun.proxy.$Proxy64.save(Unknown Source) ~[na:na]
    at com.codejava.shoptest.controller.ProductController.createProduct(ProductController.java:33) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    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.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) ~[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.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
    at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

Вот ошибка в инструментах разработчика в Chrome:

Angular is running in development mode. Call enableProdMode() to enable production mode.
product-form.component.ts:44 Categories[{"id":1,"categoryName":"cellphones"},{"id":2,"categoryName":"laptops"}]
client:52 [WDS] Live Reloading enabled.

POST http://localhost:8080/products 500
scheduleTask @ zone-evergreen.js:2845
scheduleTask @ zone-evergreen.js:385
onScheduleTask @ zone-evergreen.js:272
scheduleTask @ zone-evergreen.js:378
scheduleTask @ zone-evergreen.js:210
scheduleMacroTask @ zone-evergreen.js:233
scheduleMacroTaskWithCurrentZone @ zone-evergreen.js:1134
(anonymous) @ zone-evergreen.js:2878
proto.<computed> @ zone-evergreen.js:1449
(anonymous) @ http.js:1776
_trySubscribe @ Observable.js:42
subscribe @ Observable.js:28
subscribeToResult @ subscribeToResult.js:9
_innerSub @ mergeMap.js:59
_tryNext @ mergeMap.js:53
_next @ mergeMap.js:36
next @ Subscriber.js:49
(anonymous) @ subscribeToArray.js:3
_trySubscribe @ Observable.js:42
subscribe @ Observable.js:28
call @ mergeMap.js:21
subscribe @ Observable.js:23
call @ filter.js:13
subscribe @ Observable.js:23
call @ map.js:16
subscribe @ Observable.js:23
onSubmit @ product-form.component.ts:32
ProductFormComponent_Template_form_ngSubmit_2_listener @ product-form.component.html:3
executeListenerWithErrorHandling @ core.js:14316
wrapListenerIn_markDirtyAndPreventDefault @ core.js:14351
schedulerFn @ core.js:24822
__tryOrUnsub @ Subscriber.js:183
next @ Subscriber.js:122
_next @ Subscriber.js:72
next @ Subscriber.js:49
next @ Subject.js:39
emit @ core.js:24791
onSubmit @ forms.js:4534
NgForm_submit_HostBindingHandler @ forms.js:4567
executeListenerWithErrorHandling @ core.js:14316
wrapListenerIn_markDirtyAndPreventDefault @ core.js:14351
(anonymous) @ platform-browser.js:582
invokeTask @ zone-evergreen.js:399
onInvokeTask @ core.js:27425
invokeTask @ zone-evergreen.js:398
runTask @ zone-evergreen.js:167
invokeTask @ zone-evergreen.js:480
invokeTask @ zone-evergreen.js:1621
globalZoneAwareCallback @ zone-evergreen.js:1647
Show 19 more frames



ERROR HttpErrorResponse {headers: HttpHeaders, status: 500, statusText: "OK", url: "http://localhost:8080/products", ok: false, …}error: error: "Internal Server Error"message: ""path: "/products"status: 500timestamp: "2020-09-05T09:15:57.997+00:00"__proto__: constructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ ()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}lazyInit: () => {…}arguments: (...)caller: (...)length: 0name: ""__proto__: ƒ ()apply: ƒ apply()arguments: (...)bind: ƒ bind()call: ƒ call()caller: (...)constructor: ƒ Function()length: 0name: ""toString: ƒ toString()Symbol(Symbol.hasInstance): ƒ [Symbol.hasInstance]()get arguments: ƒ ()set arguments: ƒ ()get caller: ƒ ()set caller: ƒ ()__proto__: Object[[FunctionLocation]]: <unknown>[[Scopes]]: Scopes[0][[FunctionLocation]]: http.js:77[[Scopes]]: Scopes[4]lazyUpdate: nullnormalizedNames: Map(0)[[Entries]]No propertiessize: (...)__proto__: Map__proto__: Objectmessage: "Http failure response for http://localhost:8080/products: 500 OK"name: "HttpErrorResponse"ok: falsestatus: 500statusText: "OK"url: "http://localhost:8080/products"__proto__: HttpResponseBase

       

Я подозреваю, что angular не может проанализировать идентификатор категории в базе данных.

Как правильно разместить такие объекты? В чем моя ошибка?

Вот код Angular:

Product.ts

export class Product {
    id: string;
    brand: string;
    name: string;
    category: ProductCategory;
}

Product-category.ts

export class ProductCategory {
    id: number;
    categoryName: string;
}

Product-form.component.html

    <div class="card my-5">
    <div class="card-body">
      <form (ngSubmit)="onSubmit()" #productForm="ngForm">
        <div class="form-group">
          <label for="name">Brand</label>
          <input type="text" [(ngModel)]="product.brand" 
            class="form-control" 
            id="name" 
            name="name" 
            placeholder="Enter your name"
            required #name="ngModel">
        </div>
        
        <div class="form-group">
          <label for="email">Name</label>
          <input type="text" [(ngModel)]="product.name" 
            class="form-control" 
            id="name" 
            name="name" 
            placeholder="Enter your name"
            required #name="ngModel">
          <div [hidden]="!name.pristine" class="alert alert-danger">name is required</div>
        </div>

        <div class="form-group">
          <label for="category">Category id</label>
          <select [(ngModel)]="product.category" name="category.id">
            <option *ngFor="let category of productCategories">{{ category.id }}</option>
          </select>
        </div>
        <button type="submit" [disabled]="!productForm.form.valid" 
          class="btn btn-info">Submit</button>
      </form>
    </div>
  </div>

Product-form.component.ts

   import { Component, OnInit } from '@angular/core';
import { Product } from 'src/app/common/product';
import { ActivatedRoute, Router } from '@angular/router';
import { ProductService } from 'src/app/services/product.service';
import { ProductCategory } from 'src/app/common/product-category';

@Component({
  selector: 'app-product-form',
  templateUrl: './product-form.component.html',
  styleUrls: ['./product-form.component.css']
})
export class ProductFormComponent implements OnInit {


  product: Product;
  productCategories: ProductCategory[];

  constructor(private route: ActivatedRoute,
    private router: Router,
    private productService: ProductService) {
      this.product = new Product();
     }

  ngOnInit(): void {
    this.listProductCategories();
  }

  onSubmit(){
    this.productService.save(this.product).subscribe(
      result => this.gotoProductList()
    );
  }

  gotoProductList(): void {
   this.router.navigate(['/products']);
  }

  listProductCategories() {
    this.productService.getProductCategories().subscribe(
      data => {
        console.log('Categories' + JSON.stringify(data));
        this.productCategories = data;
      }
    );

}
}

product.service.ts

   import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Product } from '../common/product';
import { Observable } from 'rxjs';
import { ProductCategory } from '../common/product-category';


@Injectable({
  providedIn: 'root'
})
export class ProductService {
  

  private productsUrl: string;
  private categoryUrl: string;

  constructor(private http: HttpClient) { 
    this.productsUrl= 'http://localhost:8080/products';
    this.categoryUrl = 'http://localhost:8080/categories';
  }

  public findAll(): Observable<Product[]>{
    return this.http.get<Product[]>(this.productsUrl);
  }

  public save(product: Product){
    return this.http.post<Product>(this.productsUrl, product);
  }

  getProductCategories(): Observable<ProductCategory[]> {
   return this.http.get<ProductCategory[]>(this.categoryUrl);
  }
}

person JohnSmithJohn    schedule 02.09.2020    source источник


Ответы (3)


Я думаю, что в вашем файле Product.ts вы должны объявить категорию как объект типа, а не как число.

export class Product {
  id: string;
  brand: string;
  name: string;
  category : Object;
}

Затем на странице html используйте раскрывающийся список для выбора категории, которая будет списком объектов категории (получите этот список с помощью отдельного вызова службы внутри ngOnInit), а затем при сохранении объекта продукта убедитесь, что вы установили объект категории внутри объекта Product.

Спасибо.

person sandeep23    schedule 02.09.2020
comment
Спасибо за ваш совет. Я действительно как ты сказал, но у меня есть еще одно исключение. Я отредактировал свой пост и добавил туда новую информацию. Не могли бы вы взглянуть? - person JohnSmithJohn; 02.09.2020
comment
Не уверен, но можете ли вы попробовать добавить еще один конструктор в свой класс ProductCategory с двумя аргументами? общедоступная категория продукта (int id, String name) {this.id = id; this.categoryName = имя; } - person sandeep23; 03.09.2020
comment
Может быть, каким-то образом из angular, 'id' категории передается как строка вместо целого числа / long, как ожидается в конце Java. Итак, как вы сказали, это работает в Postman, потому что вы отправляете id как целочисленное значение (категория: {id: 2}). Вам нужно будет отладить метод ProductService Save () и проверить, является ли значение id категории целым или строковым, прежде чем отправлять запрос в приложение загрузки Spring. Если это строка, возможно, вам нужно изменить ее на целое число. - person sandeep23; 04.09.2020
comment
Спасибо, я отредактировал свой пост и добавил ошибки из инструментов разработчика в Chrome. Не могли бы вы взглянуть, пожалуйста - person JohnSmithJohn; 05.09.2020
comment
Я попытался добавить этот журнал к своему методу: общедоступное сохранение (продукт: Продукт) {console.log (product.category.id); вернуть this.http.post ‹Product› (this.productsUrl, product); } - person JohnSmithJohn; 05.09.2020
comment
Но это дает мне еще одну ошибку в devTools в chrome: core.js: 4197 ERROR TypeError: невозможно прочитать свойство categoryName из undefined в ProductService.save (product.service.ts: 27) - person JohnSmithJohn; 05.09.2020
comment
Спасибо. Как вы сказали, angular что-то проанализировал. Итак, я изменил свой ввод на: ‹input type = number [(ngModel)] = product.category name = category.id /› он начал работать. Не работал с ‹select› - person JohnSmithJohn; 05.09.2020

Я всегда использую шаблон Data Transfer Object для хранения данных из внешнего интерфейса и преобразования их в ваши объекты JPA перед сохранением в базу данных.

Мой пример:

PostForm используется для приема данных из формы ввода пользователя.

В PostController, называемый PostService, я использовал эту службу для преобразовать его в сущность JPA Post.

Коды внешнего интерфейса для этого API находятся здесь, они также написаны на Angular 2 ( немного староват).

person Hantsy    schedule 02.09.2020
comment
Спасибо за ответ, но я новичок в Angular, поэтому мне было сложно понять ваше решение. Я изменил свой код и получил еще одну ошибку, не могли бы вы еще раз взглянуть на мой пост. Я его отредактировал. Спасибо' - person JohnSmithJohn; 02.09.2020
comment
Ваш API проблематичен. Если вы не пишете коды тестирования, попробуйте использовать curl или Postman для проверки ваших API перед обслуживанием внешнего интерфейса. - person Hantsy; 03.09.2020
comment
Я использовал почтальона. Когда я отправляю его через почтальона, он работает. Но не работает с Angular. Это была моя проблема - person JohnSmithJohn; 03.09.2020

Это потому, что когда вы отправляете это через почтальона, ваше тело запроса выглядит так:

{
            "brand": "hp",
            "name": "envy",
            "category": {
                "id": 2
            }
        }

Но через angular ваше тело выглядит так:

{
   "brand": "hp";
    "name": "envy";
    "category_id": 2;
}

Я советую вам изменить структуру классов вашего продукта. Поле категории точно.

Вы должны уделять большое внимание тому, что ожидает ваш rest api и что вы действительно отправляете. Всегда старайтесь сравнивать значения, которые вы отправляете через почтальона и через браузер. В браузере вы можете проверить это в инструментах разработчика Chrome - ›сеть.

Вы можете адаптировать свой интерфейс к своему бэкэнду или наоборот. Если вы хотите изменить бэкэнд, а не этот интерфейс, прочтите о DTO.

person KamilCz    schedule 02.09.2020
comment
Спасибо за помощь! Я изменил свой код, но у меня есть другое исключение. Я редактировал этот пост, не могли бы вы взглянуть? - person JohnSmithJohn; 02.09.2020
comment
@JohnSmithJohn измените на category: ProductCategory в продукте и будет работать - person KamilCz; 02.09.2020
comment
Я пробовал, но тоже не работает. Затем я изменил его на Object, как посоветовал sandeepkondhare. В чем моя ошибка? - person JohnSmithJohn; 03.09.2020
comment
Я бы остался с моим предложением. Какую ошибку вы получаете? - person KamilCz; 03.09.2020
comment
Когда я изменил это так: category: ProductCategory. Я получаю ту же ошибку: PropertyValueException: свойство not-null ссылается на нулевое или временное значение: com.codejava.shoptest.entity.Product.category - person JohnSmithJohn; 03.09.2020
comment
Можете ли вы показать, какой json отправляет ваше приложение angular. Вы можете проверить это в перекрытии сети в инструментах разработки сети. - person KamilCz; 03.09.2020
comment
Я отредактировал свой пост и добавил ошибки из инструментов разработчика в Chrome. Не могли бы вы взглянуть? - person JohnSmithJohn; 05.09.2020
comment
Попробуйте @JoinColumn(name = "category", nullable = false). Есть ли у вас эти категории в базе данных? Я имею в виду, у вас уже есть категории с идентификаторами 1 и 2 в вашей базе данных? - person KamilCz; 05.09.2020
comment
у меня есть этот код: @JoinColumn (name = category_id, nullable = false). Да, эти категории находятся в БД - person JohnSmithJohn; 05.09.2020
comment
Попробуйте просто category not caregory_id, потому что в теле вашего запроса есть category var. или почитайте про DTO. - person KamilCz; 05.09.2020
comment
если он работает в почтальоне, я думаю, что проблема в коде angular. В частности, с помощью html-кода или product.service.ts. Я полагаю, он не может проанализировать категорию. Может это так - person JohnSmithJohn; 05.09.2020
comment
Я изменил, как вы сказали, категорию, она вообще перестала показывать категории в Нагуляре. Но каким-то образом мне удалось решить свою проблему. Я изменил ввод на это: ‹input type = number [(ngModel)] = product.category name = category.id /›, и он начал работать. Не работал с ‹selecct› - person JohnSmithJohn; 05.09.2020