使用@OneToOne 注释保存外键时出现问题。保存为空

Problem with saving foreign key with @OneToOne annotation. Saving as null

我有两个实体(ProjectOtherData)和一个抽象实体。我正在使用 MySQL 和 Quarkus 框架。

问题: 当我尝试保存 Project 实体字段时 project_id 仍然是 null.

Table 架构:



在下一张图片中,fk constraint in "project_other_data" table:

抽象实体:

@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 一个带有嵌入式 OtherData 的新 Project 来重现该问题。我用于 POST:

的正文
{
    "name": "John",
    "surname": "Doe",
    "otherData": {}
}

要点是:数据库实体也用作DTO。因此,请求主体的 otherData 中的字段 project 设置为 null (因为没有 Project 被传递,这将是一个递归无限定义)。

在处理从 rest 控制器到服务到存储库的实体期间,从未设置 otherDataproject。一个快速的修复方法是修改 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 Binding serialization error javax.json.bind.JsonbException: Unable to serialize property 'otherData' from com.nikitap.org_prod.entities.Project

...

Caused by: javax.json.bind.JsonbException: Recursive reference has been found in class class com.nikitap.org_prod.entities.Project.

对象结构是循环的(project 引用 otherData,return 引用 project,...)并且 Jackson 无法解析此循环。

要解决此问题,我建议将 DTO 和数据库实体分开并在它们之间显式映射。本质上:

  • 构造 Dto-object 以表示 JSON-Request 和您希望收到的响应,按 non-cyclic 顺序
  • 将 JSON-related 注释从数据库实体 类 转移到 DTO 类
  • 在服务或repository-layer(您的选择)中,将 DTO 映射到数据库实体,设置所有字段(包括从 projectotherData 和 vice-versa)
  • 在同一层中,将 database-entites 映射回 non-cyclic DTO
  • Return 来自 REST 端点的 DTO