首页 > 解决方案 > 使用 @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;
    }
}

标签: javaone-to-onequarkusjpa-annotations

解决方案


我能够通过嵌入POST一个新的. 我用于的身体:ProjectOtherDataPOST

{
    "name": "John",
    "surname": "Doe",
    "otherData": {}
}

重点是:数据库实体也用作DTO。因此,请求正文中的字段被设置为project(因为 no被传递,这将是一个递归的无限定义)。otherDatanullProject

在处理从 rest 控制器到服务到存储库的实体期间,永远不会设置projectof 。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 映射到数据库实体,设置所有字段(包括来自projectto的引用,otherData反之亦然)
  • 在同一层中,将数据库实体映射回非循环 DTO
  • 从 REST 端点返回 DTO

推荐阅读