问题描述
我有两个实体(Project
,OtherData
)和一个抽象实体。我正在使用MySQL和Quarkus框架。
问题::当我尝试保存Project
实体字段时,project_id
仍然是null
。
表架构:
下一张照片显示了“ project_other_data”表中的fk约束:
抽象实体:
@MappedSuperclass
public class AbstractEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
// getters and setters
}
项目实体
@Entity
@Table(name = "projects")
public class Project extends AbstractEntity {
@NotNull
@Column(name = "name")
private String name;
@NotNull
@Column(name = "surname")
private String surname;
@Column(name = "date_create")
@JsonbDateFormat(value = "yyyy-MM-dd")
private LocalDate dateCreate;
@Column(name = "date_update")
@JsonbDateFormat(value = "yyyy-MM-dd")
private LocalDate dateUpdate;
@OneToOne(mappedBy = "project",cascade = CascadeType.ALL)
private OtherData otherData;
// getters and setters
}
OtherData实体
@Entity
@Table(name = "project_other_data")
public class OtherData extends AbstractEntity {
@OneToOne
@JoinColumn(name = "project_id")
private Project project;
@Column(name = "days_in_year")
private Integer daysInYear;
@Column(name = "holidays_in_year")
private Integer holidaysInYear;
@Column(name = "weeks_in_year")
private Integer weeksInYear;
@Column(name = "free_saturdays")
private Integer freeSaturdays;
@Column(name = "downtime_coefficient")
private BigDecimal downtimeCoefficient;
@Column(name = "changes")
private Integer changes;
// getters and setters
}
使用代码保存实体:
@Path("projects")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ProjectRest {
@Inject
ProjectService projectService;
@POST
public Response saveProject(Project project) {
return Response.ok(projectService.saveProject(project)).build();
}
}
@RequestScoped
@Transactional
public class ProjectService {
@Inject
EntityManager entityManager;
public Project saveProject(Project project) {
if (project.getId() == null) {
entityManager.persist(project);
} else {
entityManager.merge(project);
}
return project;
}
}
解决方法
通过POST
嵌入了Project
的新OtherData
,我得以重现该问题。我用于POST
的正文:
{
"name": "John","surname": "Doe","otherData": {}
}
要点是:数据库实体也用作DTO。因此,project
中用于请求正文的字段otherData
设置为null
(因为没有传递任何Project
,这将是递归的无限定义)。
在处理从其余控制器到服务再到存储库的实体期间,project
中的otherData
从未设置。一种快速解决方案是按以下方式修改ProjectService::saveProject
:
public Project saveProject(Project project) {
project.getOtherData().setProject(project); // This line was added
if (project.getId() == null) {
entityManager.persist(project);
} else {
entityManager.merge(project);
}
return project;
}
这将解决数据库问题(将设置project_id
),但会导致下一个问题。由于出现
org.jboss.resteasy.spi.UnhandledException:javax.ws.rs.ProcessingException:RESTEASY008205:JSON绑定序列化错误javax.json.bind.JsonbException:无法从com.nikitap.org_prod.entities序列化属性'otherData' 。项目
...
由于:javax.json.bind.JsonbException:在类com.nikitap.org_prod.entities.Project中找到了递归引用。
对象结构是循环的(project
引用otherData
,返回引用project
,...),杰克逊无法解决此循环。
要解决此问题,我建议将DTO和数据库实体分开,并在它们之间显式映射。本质上:
- 以非循环顺序构造Dto对象以表示您希望接收的JSON-Request和-Response
- 将与JSON相关的注释从数据库实体类传输到DTO类
- 在服务层或存储层(您的选择)中,将DTO映射到数据库实体,设置所有字段(包括从
project
到otherData
的引用,反之亦然) - 在同一层中,将数据库实体映射回非循环DTO
- 从REST端点返回DTO