Spring Data REST调用以空变量保存对象 [英] Spring Data REST call to `save` receiving object with null variable

查看:92
本文介绍了Spring Data REST调用以空变量保存对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在继续开发一个系统,该系统在前端使用React JavaScript库(和相关的东西),在后端使用Spring Data REST,Hibernate,PostgreSQL和相关的东西。

I'm continuing the development of a system that uses the React JavaScript library (and related stuff) on the front end and Spring Data REST, Hibernate, PostgreSQL and related stuff on the back end.

该系统将由可能拥有一个或多个公司及其客户的人使用。这意味着大多数/所有模型对象都将引用它们所属的 Company (ies)。另外,公司所有者将有一些 Employee s可以在此系统上具有更高级别的访问权限(或者这些所有者本身)。

This system will be used by people who may own one or more companies and their clients. This means that most/all model objects will have a reference to the Company(ies) that they belong to. Also, company owners will have a few Employees that will have higher level access on this system (or these will be the owners themselves).

我需要实现一个功能,在该功能中,当将公司插入数据库时​​,也会插入一个员工。另外,如果一个失败,那么两个都必须失败。由于模型的设置方式,我要发送要保存的 Employee 对象,并在其中发送新的 Company ,像这样(使用Axios):

I need to implement a functionality where, when a company is inserted in the database, an employee is inserted as well. Also, if one fails, both must fail. Because of how the model was set up, I'm sending an Employee object to be saved, and, within it, the new Company, like this (using Axios):

employee: {
    // ...,
    company: {
        // ....
    }
}

问题是,当在后端调用 save 方法时, $ c Company 成员 Employee 对象为 null 。我尝试了一些事情,例如弄乱关系,将 Employee 列表添加到 Company 对象, Company 对象,但没有任何作用。

Problem is, when the save method is called in the back end, the Company member of the Employee object is null. I've tried a few things, like messing with the relationship, adding an Employee list to the Company object, passing the Company object separately, but nothing worked.

我还能尝试什么?以下是一些类:

What else could I try? Here are some classes:

package xxx.model.common;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.validation.constraints.NotNull;

import lombok.Data;

@Data
@MappedSuperclass
public abstract class Record {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;

    @NotNull
    @Column(name = "deleted")
    protected Boolean isDeleted = false;

    @NotNull
    @Column(name = "enabled")
    protected Boolean isEnabled = true;
}



Company.java



Company.java

package xxx.model;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;

import com.fasterxml.jackson.annotation.JsonManagedReference;
import xxx.common.Record;
// ...

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper=false)
@Entity
@Table(name="company")
@AttributeOverrides( { @AttributeOverride(name = "id", column = @Column(name = "id_company")) } )
public class Company extends Record {

    /*
     * ...
     */

    // Necessary for Hibernate
    protected Company() {}

    public Company(/* ... */) {
        /*
         * ...
         */
    }
}



Registry.java



Registry.java

package xxx.model.common;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.validation.constraints.NotBlank;

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper=false)
@MappedSuperclass
public abstract class Registry extends Record {

    @NotBlank
    @Column(name = "code", length = 15)
    protected String code;

    @NotBlank
    @Column(name = "name", length = 40)
    protected String name;
}



RegistrySingleCompany.java



RegistrySingleCompany.java

package xxx.model.common;

import javax.persistence.CascadeType;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;

import com.fasterxml.jackson.annotation.JsonBackReference;
import xxx.model.Company;

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper=false)
@MappedSuperclass
public class RegistrySingleCompany extends Registry {

    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE }, optional= false)
    @JoinColumn(name="id_company")
    protected Company company;
}



Employee.java



Employee.java

package xxx.model;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import xxx.model.common.RegistrySingleCompany;

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper=false)
@Entity
@Table(name="employee")
@AttributeOverrides( { @AttributeOverride(name = "id", column = @Column(name = "id_employee")) } )
public class Employee extends RegistrySingleCompany {

    /*
     * ...
     */

    // Necessary for Hibernate
    protected Employee() {}
}



EmployeeRepositoryCustom.java



EmployeeRepositoryCustom.java

package xxx.repository.custom;

import org.springframework.data.repository.query.Param;

import xxx.model.Employee;

public interface EmployeeRepositoryCustom {

    <S extends Employee> S save(S entity);
}



EmployeeRepositoryCustomImpl.java



EmployeeRepositoryCustomImpl.java

package xxx.repository.custom;

import javax.persistence.PersistenceContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestBody;

import xxx.model.Employee;

public class EmployeeRepositoryCustomImpl implements EmployeeRepositoryCustom {

    @Override
    @Transactional
    public <S extends Employee> S save(@RequestBody S entity) {
        /*
         * ...
         */
        return entity;
    }
}



EmployeeProjection.java



EmployeeProjection.java

package xxx.model.projection;

import org.springframework.data.rest.core.config.Projection;

import xxx.model.Employee;

@Projection(name = "employeeProjection", types = { Employee.class }) 
public interface EmployeeProjection {

    Boolean getIsDeleted();

    Boolean getIsEnabled();

    String getCode();

    String getName();

    /*
     * ...
     */
}



EmployeeRepository.java



EmployeeRepository.java

package xxx.repository;

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

import xxx.model.Employee;
import xxx.model.projection.EmployeeProjection;
import xxx.repository.custom.EmployeeRepositoryCustom;

@RepositoryRestResource(collectionResourceRel = "employee", path = "employees", excerptProjection = EmployeeProjection.class)
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long>, EmployeeRepositoryCustom {}

谢谢。

编辑: 添加了缺少的类。

推荐答案

如前所述,我尝试做的一件事情是添加<$ c Company 对象内的$ c> Employee 列表,这意味着使用 Company 的存储库而不是 Employee 的那个来保存两个对象,但是另一个对象也到达了null。但是,我的同事发现,通过在 @RepositoryRestResource()中使用 exported = false

As mentioned before, one of the things I tried was to add an Employee list inside the Company object, which implies using Company's repository instead of the Employee's one to save both objects, but the other object was also arriving null. However, my colleague found out that, by using exported = false inside @RepositoryRestResource(), the value would be received correctly.

这会搞砸其他事情,所以我们找到了以下临时解决方案:

That would screw other things up, so we found the following temporary solution:


  • 创建一个 exported = false 存储库( EmployeeWrapper ),其唯一目的是交付必要的 Employee 数据在 save 内构造一个新数据。

  • 而不是添加公司中的c $ c> Employee 列表,添加 EmployeeWrapper 列表。

  • EmployeeWrapper 也引用 Company

  • Create an exported = false repository (EmployeeWrapper) for the sole purpose of delivering the necessary Employee data to construct a new one inside save.
  • Instead of adding an Employee list inside Company, add an EmployeeWrapper list.
  • EmployeeWrapper also references Company.

我们仍在努力寻找更正确的方法。

We're still working on a more correct approach.

更新:更正确方法:

我的同事还发现,通过添加 @Transient Employee 列表添加到公司,可能会收到正确填写的 Employee 对象以保存它。我不知道它是否可以在存储库中使用,因为由于其他限制,我们转而使用 @RepositoryRestController 并收到了 Company 作为 @RequestBody org.springframework.hateoas.Resource< Company>资源

My colleague also found out that, by adding a @Transient Employee list to Company, it's possible to receive the correctly filled out Employee object to save it. I don't know if it works at the repository since, due to other constraints, we moved to use a @RepositoryRestController and are receiving the Company as @RequestBody org.springframework.hateoas.Resource<Company> resource.

我们仍然希望找到更好的解决方案,因为 Employee 列表在我们的模型中没有计划在 Company 内部,更糟糕的是,我们需要将其他事物列表用于其他方法。

We still want to find a better solution, because an Employee list inside Company wasn't planned in our model and, worse yet, we're needing to use list of other things for other methods.

更新::一种更好的方法:

进行了更多的实验,我们创建了一个POJO,其中包含我们需要的实体并以与以前相同的方式在控制器中收到该消息。效果很好。

Experimenting a little more, we created a POJO containing the entities that we needed and received that in the controller, same way as before. Works well.

不过,我们仍然不满意。理想情况下,我们希望接收要保存的 Employee Company 并保存在其中一次。

We're still not satisfied, though. Ideally, we want to receive the Employee to be saved, with the Company to be saved inside it, and save them both at once.

这篇关于Spring Data REST调用以空变量保存对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆