Spring Data REST调用以空变量保存对象 [英] Spring Data REST call to `save` receiving object with null variable
问题描述
我正在继续开发一个系统,该系统在前端使用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 Employee
s 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 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 necessaryEmployee
data to construct a new one insidesave
. - Instead of adding an
Employee
list insideCompany
, add anEmployeeWrapper
list. EmployeeWrapper
also referencesCompany
.
我们仍在努力寻找更正确的方法。
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屋!