Quarkus Panache OneToMany 关系未保存在数据库中
Quarkus Panache OneToMany relation is not persisted in database
我目前正在修改一个简单的 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”列都是空的。我很确定这是一个初学者问题,但在检查了多个样本后,我就是找不到我的代码有什么问题。
我遇到了同样的问题,通过将父对象设置为子对象解决了这个问题。
我有 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();
}
}
There are several things to note on the aforementioned mapping:
The @ManyToOne association uses FetchType.LAZY because, otherwise, we’d fall back to EAGER fetching which is bad for performance.
The parent entity, Post, features two utility methods (e.g. addComment and removeComment) which are used to synchronize both sides
of the bidirectional association. You should always provide these
methods whenever you are working with a bidirectional association as,
otherwise, you risk very subtle state propagation issues.
The child entity, PostComment, implement the equals and hashCode methods. Since we cannot rely on a natural identifier for equality
checks, we need to use the entity identifier instead for the equals
method. However, you need to do it properly so that equality is
consistent across all entity state transitions, which is also the
reason why the hashCode has to be a constant value. Because we rely on
equality for the removeComment, it’s good practice to override equals
and hashCode for the child entity in a bidirectional association
.
我目前正在修改一个简单的 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”列都是空的。我很确定这是一个初学者问题,但在检查了多个样本后,我就是找不到我的代码有什么问题。
我遇到了同样的问题,通过将父对象设置为子对象解决了这个问题。 我有 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();
}
}
There are several things to note on the aforementioned mapping:
The @ManyToOne association uses FetchType.LAZY because, otherwise, we’d fall back to EAGER fetching which is bad for performance.
The parent entity, Post, features two utility methods (e.g. addComment and removeComment) which are used to synchronize both sides of the bidirectional association. You should always provide these methods whenever you are working with a bidirectional association as, otherwise, you risk very subtle state propagation issues.
The child entity, PostComment, implement the equals and hashCode methods. Since we cannot rely on a natural identifier for equality checks, we need to use the entity identifier instead for the equals method. However, you need to do it properly so that equality is consistent across all entity state transitions, which is also the reason why the hashCode has to be a constant value. Because we rely on equality for the removeComment, it’s good practice to override equals and hashCode for the child entity in a bidirectional association
.