3

I have 2 entities in a JPA project:

A category and a question. so each Category will have a list of questions and each question will be part of a category (OnetoMany relation). I manage the bi-directional relationship through the set/add methodes in both entities :

Question :

    @ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "Qcategory")
private Category category;

public void setCategory(Category category) {
 this.category = category;

 if (category != null && !category.getQuestions().contains(this)) {
 category.addQuestion(this);
 }
 }

Category :

@OneToMany(cascade = { CascadeType.ALL }, mappedBy = "category")
private List<Question> questions= new ArrayList<Question>();


 public void addQuestion(Question question) {
 this.questions.add(question);

 if (question.getCategory() != this) {
 question.setCategory(this);
 }

 }

I first create a category.

Category category1 = new Category();
category1.setName = "exampleCategory";

I add this to the db through my repository (added in a similar way as the question addOrUpdate as below)

After that I create an question

Question question1 = new Question();

I set the category of the question to category1

question.setCategory = category1;

After this I also try to persist the question to the db by calling the addOrUpdate method below. I then get an error :

....:javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: jpa.entities.Category

I use a repository method like :

@Override
public boolean addOrUpdate(Question question) {
    EntityManagerFactory emf = JPARepositoryFactory
            .getEntityManagerFactory();
    EntityManager em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    tx.begin();

    Question tempQuestion = null;
    try {
        if (question.getId() != null) {
            tempQuestion = em.find(Question.class,
                    question.getId());
        }

        if (tempQuestion == null) {
            em.persist(question);
        } else {

            tempQuestion .setCategory(question.getCategory());
            ... (other setters)
            tempQuestion = em.merge(question);
        }
    } catch (Exception e) {
        ....logging...      }
    tx.commit();
    em.close();
    emf.close();
    return true;
}

Any suggestion would be more then welcome.

6
  • Did you ever call persist/merge on the category? Commented Mar 11, 2013 at 2:27
  • You are setting values for tempQuestion but merging question or is this just a typo. Commented Mar 11, 2013 at 5:03
  • @Pace Not directly, I only put the relation to the category. But I realize I also add this question to the list in the category. But I use CascadeType.ALL on the entity's who have a relation. So I believe he would do this. Commented Mar 11, 2013 at 6:52
  • @NayanWadekar Not a typo ... I think I use it correctly? Commented Mar 11, 2013 at 7:22
  • @NayanWadekar I believe this is the way to work with a detached entity?! Commented Mar 11, 2013 at 7:50

3 Answers 3

1

So you're only allowed to call persist once on an entity. That error means that you've already called persist on that Question object that is being passed in, but you did so in another transaction. If you want to reattach the Question object to the persistence context you need to call merge or reload it from the database.

Sign up to request clarification or add additional context in comments.

5 Comments

I first create an category and save it to the db (persist). After that I create a question and I set the question.setCategory to the category I created. Then I try to save this new question to the db (with a reference to teh allready excisting category) I keep both ends of the relationship at the right state. I believe this is a "normal" way to work with these realtionships. You create one object that is needed in the other and select that before I create the other.
Ah. But you have a persist cascade on your question. This means it goes and calls persist on the category which has already been persisted, thus the message. Remove the persist cascade from the Question object's category field.
This seems to do the trick. what Cascade I need to put in? I changed it to Cascade.MERGE but I need to evaluate if that's ok
You only want cascade persist if you create all of the objects within the same transaction. A typical example is something like an order which has several line items. If you want to create the order and all the line items at the same time then you could put Cascade.PERSIST on the order. Typically you don't have any cascades on the many side of a relationship.
Thanks! I will try to put off all cascade on the many sides of the relationships (also no need for MERGE??). Any suggestion what type of cascade to put on the other side (one)?
0

What you have to do before persist or merge is to set the Category reference in each Question .

2 Comments

But I do so when I write : question.setCategory = category1; ?
I added the bi-directional setting to the post. I believe I do this correctly
0

[Note: This may be not the direct answer, just few observations]

  1. It's definitely isn't a good practice to initialize EntityManagerFactory with each method invocation. Instead, it should be created once probably during application startup.

  2. You are passing Category, which is part of another persistence context to addOrUpdate in which it isn't in managed state.

  3. What do you have cascade=MERGE/cascade=PERSIST or cascade=ALL on your relationship.

  4. Probably, you can fetch Category by id again in the current thransaction & set it in question before persisting.

From Documentation :

Bidirectional relationships must follow these rules.

  • The inverse side of a bidirectional relationship must refer to its owning side by using the mappedBy element of the @OneToOne, @OneToMany, or @ManyToMany annotation. The mappedBy element designates the property or field in the entity that is the owner of the relationship.
  • The many side of many-to-one bidirectional relationships must not define the mappedBy element. The many side is always the owning side of the relationship.
  • For one-to-one bidirectional relationships, the owning side corresponds to the side that contains the corresponding foreign key.
  • For many-to-many bidirectional relationships, either side may be the owning side.

7 Comments

I have Cascade= CascadeType.ALL in my relationship (see relationship at the top of my question). I believe I followed the rules for this relatyionship. How do I fetch the id again, if you see at my addOrUpdate method?
@DarthBlueRay It's similar to what you have done to fetch tempQuestion through EntityManager, something like em.find(Question.class, category.getId()) & then set it in question. Can try your current code with single transaction propagating. Creating category & then persisteing it, then setting it in question & persist. Can change persist/merge operations accordingly.
I changed the code to : tempCategory = em.find(Category.class, question.getCategory().getId()); tempFeedbackVraag.setCategory(tempCategory); If this what you mean. I got the same error ... .
I'm wrong doing so. first I create a new category what I persist. Then I create a new question. then I set this category to the question (set) and then try to persist this. In this case the question it self is new (must be persisted) but the property category has a link to an all ready persisted category (hence the detached object). When adding the new question I perist, I don't merge ... . I will fetch it and try to set it just before persisting.
I tried to fetch the category like : tempCategory = em.find(Category.class, question.getCategory().getId()); question.setCategory(tempCategory); em.persist(question); I still get the same error.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.