jakarta-eejpajpqlpersistence-manager

JPA cascade persist error with an entity that is already persisted


I've got the error below trying to update an entity. The solution to this problem says that the "owned" entity has to be persisted before persisting the owner but I did that. Or to mark both entity with cascade.

error (I guess the full stacktrace isn't needed):

2015-08-10T20:45:11.481+0200|Warning: A system exception occurred during an invocation on EJB ThreadLookUpService, method: public void main.java.services.ThreadLookUpService.setLastPost(long,main.java.entities.Post)
Caused by: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: main.java.entities.Post@6724f919.

( If I add cascade = CascadeType.ALL I get another error, telling me that the foreign key doesn't exist but I just persisted it. As far as I understand I shouldn't even have to use cascade if I persist the entities in the right order.

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`forumcs`.`thethreads`, CONSTRAINT `fk_thethreads_posts1` FOREIGN KEY (`last_post`) REFERENCES `posts` (`idpost`) ON DELETE NO ACTION ON UPDATE NO ACTION)

)

Here I persist post(owned) then Thethread(owner):

post = postLookup.createPostAndGiveItBack(post);
threadLookup.setLastPost(thread.getThread().getIdthread(), post); //this gives error.

Here is my method to persist post, I used flush because I thought that post id wasn't generated or smtg:

@Override
public Post createPostAndGiveItBack(Post post) {
    em.persist(post);
    em.flush();
    return post;
}

Here is my method to update the lastPost of Thethread. I could merge it directly with the one I pulled out before but if I did do that then my upvote and downvote field will be messed up if 2 different user watch the same page and update the thread :

@Override
public void setLastPost(long id, Post post) {
    Query query = em.createQuery(FIND_THREAD_BY_ID);
    query.setParameter("id", id);
    Thethread thread = (Thethread) query.getSingleResult();
    thread.setLastPost(post);
}

Post entity:

@Entity
@Table(name = "posts")
@NamedQuery(name = "Post.findAll", query = "SELECT p FROM Post p")
public class Post implements Serializable, Comparable<Post> {
    private static final long serialVersionUID = 1L;

    @Id
    private int idpost;

    private String content;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "date_post")
    private Date datePost;

    private int downvotes;

    private int upvotes;

    // bi-directional many-to-one association to PostVote
    @OneToMany(mappedBy = "post")
    private List<PostVote> postVotes;

    // bi-directional many-to-one association to Post
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "reply_to_post")
    private Post replyTo;

    // bi-directional many-to-one association to Post
    @OneToMany(mappedBy = "replyTo")
    private List<Post> replies;

    // bi-directional many-to-one association to Thethread
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "threads_idthread")
    private Thethread thethread;

    // bi-directional many-to-one association to User
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "users_username")
    private User user;

    @Transient
    private double confidenceScore;

    public Post() {
    }

Thethread Entity:

@Entity
@Table(name = "thethreads")
@NamedQuery(name = "Thethread.findAll", query = "SELECT t FROM Thethread t")
public class Thethread implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    private int idthread;

    private String content;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "date_posted")
    private Date datePosted;

    // I could get this by scanning the Threadvotes and count the number of downvotes
   // But I thought it was better like this for ease and performance.
    private int downvotes;

    @Column(name = "hot_score")
    private int hotScore;

    @Column(name = "is_pol")
    private String isPol;

    private String title;

    private String type;

    private int upvotes;

    // bi-directional many-to-one association to Post
    @OneToMany(mappedBy = "thethread")
    private List<Post> posts;

    // bi-directional many-to-one association to Category
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "categories_idcategory")
    private Category category;

    // UNI-directional many-to-one association to Post
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "last_post")
    private Post lastPost;

    // bi-directional many-to-one association to User
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "posted_by")
    private User user;

    // bi-directional many-to-one association to ThreadVote
    @OneToMany(mappedBy = "thethread")
    private List<ThreadVote> threadVotes;

Solution

  • I added this to my entity ID:

    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    

    and it works.