java - 使用 @OneToOne 注释保存外键的问题。保存为空
问题描述
我有两个实体 ( 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
}
其他数据实体
@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
(因为 no被传递,这将是一个递归的无限定义)。otherData
null
Project
在处理从 rest 控制器到服务到存储库的实体期间,永远不会设置project
of 。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.Project 序列化属性“otherData”
...
原因:javax.json.bind.JsonbException:在类 com.nikitap.org_prod.entities.Project 中找到递归引用。
对象结构是循环的(project
引用otherData
,返回引用project
,...),杰克逊无法解决这个循环。
要解决此问题,我建议将 DTO 和数据库实体分开并在它们之间显式映射。在本质上:
- 以非循环顺序构造 Dto-object 以表示您希望接收的 JSON-Request 和 -Response
- 将与 JSON 相关的注释从数据库实体类传输到 DTO 类
- 在服务层或存储层(您的选择)中,将 DTO 映射到数据库实体,设置所有字段(包括来自
project
to的引用,otherData
反之亦然) - 在同一层中,将数据库实体映射回非循环 DTO
- 从 REST 端点返回 DTO
推荐阅读
- unreal-engine4 - 虚幻引擎 (4.25.4) 不再打开项目
- google-apps-script - 提交 Google 表单后自动从 Google 表单响应中移动特定数据
- javascript - 使用 Apollo 客户端更新缓存数据 - ReactJS
- ruby-on-rails - Rails 网络服务器在升级后崩溃
- javascript - 在 Discord.js v12+ 上向其他分片发送消息
- c# - 为什么 ML.NET 矩阵分解没有意义的预测
- azure-devops - PR 和 BatchedCI 触发器的结果好坏参半
- python - 将数据库值分配给 Python 变量
- javascript - 处理两个选择以显示/隐藏 Div
- azure-functions - 我可以在服务总线属性的哈希值和 Azure 函数的实例之间建立关联吗