【问题标题】:Quarkus Panache OneToMany relation is not persisted in databaseQuarkus Panache OneToMany 关系未保存在数据库中
【发布时间】:2021-03-09 09:03:36
【问题描述】:

我目前正在修补一个简单的 HTTP 资源。我的模型由带有多个“果实”的“树”组成。两者都继承自 PanacheEntity。

树:

    @Entity
    public class Tree extends PanacheEntity {
    public String name;

    @OneToMany(mappedBy = "tree", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    public List<Fruit> fruits = new ArrayList<>();
    }

水果

    @Entity
    public class Fruit extends PanacheEntity {

    public String name;
    public String color;
    public int cores;

    @ManyToOne
    @JsonbTransient
    public Tree tree;
    }

资源:

    import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource;

    public interface TreeResource extends PanacheEntityResource<Tree, Long> { }

这是我通过 Swagger 发送的 POST 请求

{
  "name": "Apple Tree",
  "fruits": [
    {
      "color": "green",
      "cores": 3,
      "name": "Apple2"
    },
    {
      "color": "red",
      "cores": 4,
      "name": "Apple"
    }
  ]
}

响应告诉我,为所有对象创建了 ID:

{
  "id": 4,
  "fruits": [
    {
      "id": 5,
      "color": "green",
      "cores": 3,
      "name": "Apple2"
    },
    {
      "id": 6,
      "color": "red",
      "cores": 4,
      "name": "Apple"
    }
  ],
  "name": "Apple Tree"
}

但是当我在 /trees 下调用 Get all 时,我得到以下响应:

[
  {
    "id": 1,
    "fruits": [],
    "name": "Oak"
  },
  {
    "id": 4,
    "fruits": [],
    "name": "Apple Tree"
  }
]

水果总是空的。检查 postgres 数据库显示 Fruit 中的所有“tree_id”列都是空的。我很确定这是一个初学者的问题,但是在检查了多个示例后,我找不到我的代码有什么问题。

【问题讨论】:

  • 你能准备一个独立的测试用例(一个简单的 Maven 项目重现问题)并将它发布到 Quarkus 跟踪器中吗?我认为我们需要看看这个用例(而且 Panache REST 资源相当新,因此非常欢迎提供反馈)。
  • 当然我会在周末创建一个小项目。与此同时,我通过声明“OneToMany”而不是“ManyToOne”来解决这个问题,所以现在它是单向的。
  • 所以我正要完成我的示例项目并决定再试一次,现在它至少在最初的 POST 之后工作正常。然后我尝试通过 PUT 更新我的数据,现在我再次看到同样的问题。链接到我的示例项目(基于 quarkus 官方 panache-quickstart):github.com/WeinbM/quarkus-hibernate-orm-sample

标签: java quarkus quarkus-panache


【解决方案1】:

我遇到了同样的问题,并通过将父对象设置为子对象来解决它。 我有 Job 和 JobArgs 项要持久化。

    // ... somewhere in JobArg.java
    @JsonbTransient
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "job_id", foreignKey = @ForeignKey(name = "job_id_fk"))
    public Job job;
    @OneToMany(mappedBy = "job", cascade = CascadeType.ALL, orphanRemoval = true)
    public List<JobArg> arguments = new ArrayList<>();
    // ... somewhere in Job.java 
    // I used reactive-pgclient so my method return Uni<T>
    public void addArgument(final JobArg jobArg) {
        arguments.add(jobArg);
        jobArg.job = this;
    }
    public static Uni<Job> insert(final UUID userId, final JobDto newJob) {
        final Job job = new Job();
        //... map fields from dto ...
        newJob.getArguments().stream().map(arg -> {
            final JobArg jobArg = new JobArg();
            //... map fields from dto ...
            return jobArg;
        }).forEach(job::addArgument);
        
        final Uni<Void> jobInsert = job.persist();
        final Uni<UserAction> userActionInsert = UserAction.insertAction(type, job.id, userId, null);
        return Uni.combine().all().unis(jobInsert, userActionInsert).combinedWith(result -> job);
    }

这是来自Vlad Mihalcea's blog 的示例代码: 对于双向映射:

@Entity(name = "Post")
@Table(name = "post")
public class Post {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String title;
 
    @OneToMany(
        mappedBy = "post",
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
    private List<PostComment> comments = new ArrayList<>();
 
    //Constructors, getters and setters removed for brevity
 
    public void addComment(PostComment comment) {
        comments.add(comment);
        comment.setPost(this);
    }
 
    public void removeComment(PostComment comment) {
        comments.remove(comment);
        comment.setPost(null);
    }
}
 
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String review;
 
    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;
 
    //Constructors, getters and setters removed for brevity
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PostComment )) return false;
        return id != null && id.equals(((PostComment) o).getId());
    }
 
    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
}

上述映射有几点需要注意:

@ManyToOne 关联使用 FetchType.LAZY,否则,我们会退回到 EAGER 提取,这对性能不利。

父实体 Post 具有两个实用方法(例如 addComment 和 removeComment),用于同步双方 的双向关联。您应该始终提供这些 每当您使用双向关联时的方法, 否则,您将面临非常微妙的状态传播问题。

子实体 PostComment 实现 equals 和 hashCode 方法。因为我们不能依赖自然标识符来表示相等 检查,我们需要使用实体标识符来代替等号 方法。但是,您需要正确地做到这一点,以便平等 在所有实体状态转换中保持一致,这也是 hashCode 必须是常量值的原因。因为我们依靠 removeComment 的相等性,最好覆盖 equals 以及双向关联中子实体的 hashCode

.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-09
    • 2022-11-30
    • 1970-01-01
    • 2010-10-23
    • 2019-07-08
    • 2021-11-27
    • 2011-10-26
    • 2022-01-09
    相关资源
    最近更新 更多