I have 2 entities with a relationship of OneToMany
@Entity
class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", insertable = false, updatable = false)
private Long id;
@OneToMany(mappedBy = "post", fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
Set<Comment> comments = new HashSet();
public void addComment(Comment c) {
c.setPost(this);
comments.add(c);
}
}
@Entity
class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", insertable = false, updatable = false)
private Long id;
@ManyToOne
@JoinColumn(name = "post_id")
private Post post;
}
CREATE TABLE post(
id BIGINT NOT NULL DEFAULT nextval('post_id_seq') PRIMARY KEY,
title VARCHAR(50)
);
CREATE TABLE comment (
id BIGINT NOT NULL PRIMARY KEY,
post_id BIGINT NOT NULL,
text VARCHAR(255),
CONSTRAINT fk_comment_post_id
FOREIGN KEY (post_id)
REFERENCES post (id)
);
When a save this entity everything works fine, and savedPost is a complete entity that includes the comment.
Post post = new Post();
post.setId(1L);
post.setTitle("Hello post test");
post.addComment(new Comment("Hi There");
Post savedPost = postRepository.save(post);
But, from the moment I decided to remove that sequence to generate the Post Id in the Post table, and remove the annotation @GenerateValue and set the id with a number generated by me, things start to fall
CREATE TABLE post(
id BIGINT NOT NULL PRIMARY KEY,
title VARCHAR(50)
);
@Entity
class Post {
@Id
@Column(name = "id", updatable = false)
private Long id;
...
}
Now after saving the post the savedPost object does not contains the comments any more (actually it contains one Comment with all fields null). It seems that the join is broken.
Any idea where and why it is happening?
UPDATE based on Simon Martinelli's code: github.com/simasch/69978943
If instead of .save(post)
I call .saveAndFlush(post)
the comment returns with ONLY its ID set, the rest of the fields come null. It is better than the behavior I describe, but still not good.
But if I annotate Post.id
with @GeneratedValue
and use.save(post)
, everything works perfectly.
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Long id;
CodePudding user response:
The problem can be found in the SimpleJpaRepository
save()
method
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
There it is checking if the entity is new. But as you set the ID manually it is not considered as new and therefor not EntityManger.persist()
but EntityManager.merge()
is called.
So you need to add the CascadeType.MERGE to the mapping
@OneToMany(mappedBy = "post", fetch = FetchType.EAGER,
cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<Comment> comments = new HashSet<>();