persistence@glassfish.java.net

Re: PreRemove ConcurrentModificationException

From: Greg Ederer <greg_at_ergonosis.com>
Date: Thu, 14 Jun 2007 11:51:49 -0700

I should have said:

       Object o = em.find(javaType, id);
       responseMap.put("javaType", javaType);
       responseMap.put("id", id);
       utx.begin();
       em.merge(o);
       em.joinTransaction();
       em.remove(o);
       utx.commit();

Where javaType is com.acadept.model.article.ArticlePlacement.

Greg Ederer wrote:
> Markus Fuchs wrote:
>> Hi Greg,
>>
>> That sounds like a bug to me. Could you send me your pojos and a
>> small snipplet of your application code as a test case?
>>
>> You are talking about em.remove(article) and no @PreRemove in
>> ArticlePlacement resulting in a foreign key violation, correct?
>>
> Actually, I get the fk violation when I em.remove(articlePlacement)
> w/o the @PreRemove. Does this still sound like a bug, or is it just
> my incorrect way of doing things?
>
> Code below.
>
> Cheers,
>
> Greg
>
> In my servlet:
>
> Object o = em.find(javaType, id);
> responseMap.put("javaType", javaType);
> responseMap.put("id", id);
> utx.begin();
> em.merge(o);
> em.joinTransaction();
> em.remove(o);
> utx.commit();
>
>
> /*
> * Article.java
> *
> * Created on May 7, 2007, 2:15 PM
> *
> * To change this template, choose Tools | Template Manager
> * and open the template in the editor.
> */
>
> package com.acadept.model.article;
>
> import com.acadept.model.*;
> import java.io.Serializable;
> import java.util.ArrayList;
> import java.util.Date;
> import java.util.List;
> import javax.persistence.CascadeType;
> import javax.persistence.Column;
> import javax.persistence.Entity;
> import javax.persistence.GeneratedValue;
> import javax.persistence.GenerationType;
> import javax.persistence.Id;
> import javax.persistence.ManyToOne;
> import javax.persistence.OneToMany;
> import javax.persistence.OneToOne;
> import javax.persistence.PreRemove;
> import javax.persistence.SequenceGenerator;
> import javax.persistence.Temporal;
> import javax.persistence.TemporalType;
>
> /**
> * Entity class Article
> * * @author gregederer
> */
> @Entity
> public class Article implements Serializable
> {
> @Id
> @GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
> "ARTICLE_SEQ_GENERATOR")
> @SequenceGenerator(name = "ARTICLE_SEQ_GENERATOR", sequenceName =
> "ARTICLE_ID_SEQ")
> private Long id;
>
> @ManyToOne
> private ArticleCategory articleCategory;
>
> @OneToMany(mappedBy = "article", cascade=CascadeType.ALL)
> private List<ArticlePlacement> articlePlacements = new
> ArrayList<ArticlePlacement>();
>
> @Column(name="lead_in_image")
> private String leadInImage;
>
> private String title;
>
> private String subTitle;
>
> @Column(name="lead_in", columnDefinition="text")
> private String leadIn;
>
> @Column(columnDefinition="text")
> private String body;
>
> @OneToOne
> private AppUser poster;
>
> @Column(name="publication_date")
> @Temporal(TemporalType.DATE)
> private Date createdDate;
>
> /** Creates a new instance of Article */
> public Article()
> {
> }
>
> /**
> * Gets the id of this Article.
> * @return the id
> */
> public Long getId()
> {
> return this.id;
> }
>
> /**
> * Sets the id of this Article to the specified value.
> * @param id the new id
> */
> public void setId(Long id)
> {
> this.id = id;
> }
>
> /**
> * Returns a hash code value for the object. This implementation
> computes * a hash code value based on the id fields in this object.
> * @return a hash code value for this object.
> */
> @Override
> public int hashCode()
> {
> int hash = 0;
> hash += (this.getId() != null ? this.getId().hashCode() : 0);
> return hash;
> }
>
> /**
> * Determines whether another object is equal to this Article. The
> result is * <code>true</code> if and only if the argument is not
> null and is a Article object that * has the same id field values as
> this object.
> * @param object the reference object with which to compare
> * @return <code>true</code> if this object is the same as the argument;
> * <code>false</code> otherwise.
> */
> @Override
> public boolean equals(Object object)
> {
> // TODO: Warning - this method won't work in the case the id fields
> are not set
> if (!(object instanceof Article)) {
> return false;
> }
> Article other = (Article)object;
> // If both are the same object, return true
> if(this == other)
> {
> return true;
> }
> // If both objects have ids, compare their ids
> if(this.getId() != null && other.getId() != null)
> {
> // If their ids are equal, return true
> if(this.getId().equals(other.getId()))
> {
> return true;
> }
> // Otherwise, return false
> else
> {
> return false;
> }
> }
> // If one or both do not have ids, compare their 'natural keys'
> if(this.getTitle() != null && this.getTitle().equals(other.getTitle())
> && this.getCreatedDate() != null &&
> this.getCreatedDate().equals(other.getCreatedDate())
> && this.getPoster() != null &&
> this.getPoster().equals(other.getPoster())
> )
> {
> return true;
> }
> return false;
> }
>
> /**
> * Returns a string representation of the object. This
> implementation constructs * that representation based on the id fields.
> * @return a string representation of the object.
> */
> @Override
> public String toString()
> {
> return "com.acadept.model.Article[id=" + getId() + "]";
> }
>
> public ArticleCategory getArticleCategory()
> {
> return articleCategory;
> }
>
> public void setArticleCategory(ArticleCategory articleCategory)
> {
> this.articleCategory = articleCategory;
> articleCategory.addArticle(this);
> }
>
> public String getTitle()
> {
> return title;
> }
>
> public void setTitle(String title)
> {
> this.title = title;
> }
>
> public String getSubTitle()
> {
> return subTitle;
> }
>
> public void setSubTitle(String subTitle)
> {
> this.subTitle = subTitle;
> }
>
> public String getLeadIn()
> {
> return leadIn;
> }
>
> public void setLeadIn(String leadIn)
> {
> this.leadIn = leadIn;
> }
>
> public String getBody()
> {
> return body;
> }
>
> public void setBody(String body)
> {
> this.body = body;
> }
>
> public AppUser getPoster()
> {
> return poster;
> }
>
> public void setPoster(AppUser poster)
> {
> this.poster = poster;
> }
>
> public Date getCreatedDate()
> {
> return createdDate;
> }
>
> public void setCreatedDate(Date createdDate)
> {
> this.createdDate = createdDate;
> }
>
> public String getLeadInImage()
> {
> return leadInImage;
> }
>
> public void setLeadInImage(String leadInImage)
> {
> this.leadInImage = leadInImage;
> }
>
> public List<ArticlePlacement> getArticlePlacements()
> {
> return articlePlacements;
> }
>
> public void setArticlePlacements(List<ArticlePlacement>
> articlePlacements)
> {
> this.articlePlacements = articlePlacements;
> }
>
> public void addArticlePlacement(ArticlePlacement articlePlacement)
> {
> if(this.getArticlePlacements().contains(articlePlacement))
> {
> return;
> }
> getArticlePlacements().add(articlePlacement);
> articlePlacement.setArticle(this);
> }
>
> public void removeArticlePlacement(ArticlePlacement articlePlacement)
> {
> getArticlePlacements().remove(articlePlacement);
> articlePlacement.setArticle(null);
> }
>
> @PreRemove
> public void beforeRemove()
> {
> List<ArticlePlacement> articlePlacements = getArticlePlacements();
> for(ArticlePlacement ap : articlePlacements)
> {
> ap.setArticle(null);
> }
> }
> }
>
>
> /*
> * ArticlePlacement.java
> *
> * Created on May 7, 2007, 2:32 PM
> *
> * To change this template, choose Tools | Template Manager
> * and open the template in the editor.
> */
>
> package com.acadept.model.article;
>
> import java.io.Serializable;
> import java.util.Date;
> import javax.persistence.Column;
> import javax.persistence.Entity;
> import javax.persistence.GeneratedValue;
> import javax.persistence.GenerationType;
> import javax.persistence.Id;
> import javax.persistence.ManyToOne;
> import javax.persistence.PreRemove;
> import javax.persistence.SequenceGenerator;
> import javax.persistence.Table;
> import javax.persistence.Temporal;
> import javax.persistence.TemporalType;
>
> /**
> * Entity class ArticlePlacement
> *
> * @author gregederer
> */
> @Entity
> @Table(name="article_placement")
> public class ArticlePlacement implements Serializable
> {
> @Id
> @GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
> "ARTICLE_PLACEMENT_SEQ_GENERATOR")
> @SequenceGenerator(name = "ARTICLE_PLACEMENT_SEQ_GENERATOR",
> sequenceName = "ARTICLE_PLACEMENT_ID_SEQ")
> private Long id;
>
> @ManyToOne
> private Article article;
>
> @ManyToOne
> private ArticleGroup articleGroup;
>
> @Temporal(value = TemporalType.TIMESTAMP)
> @Column(name="order_position")
> private Date position;
>
> @Temporal(TemporalType.TIMESTAMP)
> private Date startRunDate;
>
> @Temporal(TemporalType.TIMESTAMP)
> private Date endRunDate;
>
> /** Creates a new instance of ArticlePlacement */
> public ArticlePlacement()
> {
> }
>
> /**
> * Gets the id of this ArticlePlacement.
> * @return the id
> */
> public Long getId()
> {
> return this.id;
> }
>
> /**
> * Sets the id of this ArticlePlacement to the specified value.
> * @param id the new id
> */
> public void setId(Long id)
> {
> this.id = id;
> }
>
> /**
> * Returns a hash code value for the object. This implementation
> computes
> * a hash code value based on the id fields in this object.
> * @return a hash code value for this object.
> */
> @Override
> public int hashCode()
> {
> int hash = 0;
> hash += (this.getId() != null ? this.getId().hashCode() : 0);
> return hash;
> }
>
> /**
> * Determines whether another object is equal to this
> ArticlePlacement. The result is
> * <code>true</code> if and only if the argument is not null and is a
> ArticlePlacement object that
> * has the same id field values as this object.
> * @param object the reference object with which to compare
> * @return <code>true</code> if this object is the same as the argument;
> * <code>false</code> otherwise.
> */
> @Override
> public boolean equals(Object object)
> {
> if (!(object instanceof ArticlePlacement))
> {
> return false;
> }
> ArticlePlacement other = (ArticlePlacement)object;
> // If both are the same object, return true
> if(this == other)
> {
> return true;
> }
> // If both objects have ids, compare their ids
> if(this.getId() != null && other.getId() != null)
> {
> // If their ids are equal, return true
> if(this.getId().equals(other.getId()))
> {
> return true;
> }
> // Otherwise, return false
> else
> {
> return false;
> }
> }
> // If one or both do not have ids, compare their 'natural keys'
> if(this.getArticle() != null &&
> this.getArticle().equals(other.getArticle())
> && this.getArticleGroup() != null &&
> this.getArticleGroup().equals(other.getArticleGroup())
> )
> {
> return true;
> }
> return false;
> }
>
> /**
> * Returns a string representation of the object. This
> implementation constructs
> * that representation based on the id fields.
> * @return a string representation of the object.
> */
> @Override
> public String toString()
> {
> return "com.acadept.model.ArticlePlacement[id=" + getId() + "]";
> }
>
> public Article getArticle()
> {
> return article;
> }
>
> public void setArticle(Article article)
> {
> this.article = article;
> if(article != null)
> {
> article.addArticlePlacement(this);
> }
> }
>
> public ArticleGroup getArticleGroup()
> {
> return articleGroup;
> }
>
> public void setArticleGroup(ArticleGroup articleGroup)
> {
> this.articleGroup = articleGroup;
> if(articleGroup != null)
> {
> articleGroup.addArticlePlacement(this);
> }
> }
>
> public Date getPosition()
> {
> return position;
> }
>
> public void setPosition(Date position)
> {
> this.position = position;
> }
>
> public Date getStartRunDate()
> {
> return startRunDate;
> }
>
> public void setStartRunDate(Date startRunDate)
> {
> this.startRunDate = startRunDate;
> }
>
> public Date getEndRunDate()
> {
> return endRunDate;
> }
>
> public void setEndRunDate(Date endRunDate)
> {
> this.endRunDate = endRunDate;
> }
>
> @PreRemove
> public void beforeRemove()
> {
> getArticleGroup().removeArticlePlacement(this);
> if(getArticle() != null)
> {
> getArticle().removeArticlePlacement(this);
> }
> }
> }
>
>
> /*
> * ArticleGroup.java
> *
> * Created on May 7, 2007, 2:27 PM
> *
> * To change this template, choose Tools | Template Manager
> * and open the template in the editor.
> */
>
> package com.acadept.model.article;
>
> import com.acadept.model.*;
> import java.io.Serializable;
> import java.util.ArrayList;
> import java.util.List;
> import java.util.logging.Logger;
> import javax.persistence.CascadeType;
> import javax.persistence.Column;
> import javax.persistence.Entity;
> import javax.persistence.GeneratedValue;
> import javax.persistence.GenerationType;
> import javax.persistence.Id;
> import javax.persistence.OneToMany;
> import javax.persistence.OrderBy;
> import javax.persistence.SequenceGenerator;
> import javax.persistence.Table;
>
> /**
> * Entity class ArticleGroup
> * * @author gregederer
> */
> @Entity
> @Table(name="article_group")
> public class ArticleGroup implements Serializable
> {
> private static Logger logger =
> Logger.getLogger(ContentGroup.class.getName());
>
> @Id
> @GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
> "ARTICLE_GROUP_SEQ_GENERATOR")
> @SequenceGenerator(name = "ARTICLE_GROUP_SEQ_GENERATOR", sequenceName
> = "ARTICLE_GROUP_ID_SEQ")
> private Long id;
>
> @Column(name="content_name", unique=true)
> private String name;
>
> @Column(columnDefinition="text")
> private String description;
>
> @OneToMany(cascade=CascadeType.ALL)
> @OrderBy("position ASC")
> private List<ArticlePlacement> articlePlacements = new
> ArrayList<ArticlePlacement>();
>
> /**
> * Gets the id of this ArticleGroup.
> * @return the id
> */
> public Long getId()
> {
> return this.id;
> }
>
> /**
> * Sets the id of this ArticleGroup to the specified value.
> * @param id the new id
> */
> public void setId(Long id)
> {
> this.id = id;
> }
>
> /**
> * Returns a hash code value for the object. This implementation
> computes * a hash code value based on the id fields in this object.
> * @return a hash code value for this object.
> */
> @Override
> public int hashCode()
> {
> int hash = 0;
> hash += (this.getId() != null ? this.getId().hashCode() : 0);
> return hash;
> }
>
> /**
> * Determines whether another object is equal to this ArticleGroup.
> The result is * <code>true</code> if and only if the argument is not
> null and is a ArticleGroup object that * has the same id field
> values as this object.
> * @param object the reference object with which to compare
> * @return <code>true</code> if this object is the same as the argument;
> * <code>false</code> otherwise.
> */
> @Override
> public boolean equals(Object object)
> {
> // TODO: Warning - this method won't work in the case the id fields
> are not set
> if (!(object instanceof ArticleGroup)) {
> return false;
> }
> ArticleGroup other = (ArticleGroup)object;
> // If both are the same object, return true
> if(this == other)
> {
> return true;
> }
> // If both objects have ids, compare their ids
> if(this.getId() != null && other.getId() != null)
> {
> // If their ids are equal, return true
> if(this.getId().equals(other.getId()))
> {
> return true;
> }
> // Otherwise, return false
> else
> {
> return false;
> }
> }
> // If one or both do not have ids, compare their 'natural keys'
> if(this.getName() != null && this.getName().equals(other.getName())
> )
> {
> return true;
> }
> return false;
> }
>
> /**
> * Returns a string representation of the object. This
> implementation constructs * that representation based on the id fields.
> * @return a string representation of the object.
> */
> @Override
> public String toString()
> {
> return "com.acadept.model.ArticleGroup[id=" + getId() + "]";
> }
>
> public static Logger getLogger()
> {
> return logger;
> }
>
> public static void setLogger(Logger aLogger)
> {
> logger = aLogger;
> }
>
> public String getName()
> {
> return name;
> }
>
> public void setName(String name)
> {
> this.name = name;
> }
>
> public String getDescription()
> {
> return description;
> }
>
> public void setDescription(String description)
> {
> this.description = description;
> }
>
> public List<ArticlePlacement> getArticlePlacements()
> {
> return articlePlacements;
> }
>
> public void setArticlePlacements(List<ArticlePlacement>
> articlePlacements)
> {
> this.articlePlacements = articlePlacements;
> }
>
> public void addArticlePlacement(ArticlePlacement articlePlacement)
> {
> if(this.getArticlePlacements().contains(articlePlacement))
> {
> return;
> }
> getArticlePlacements().add(articlePlacement);
> articlePlacement.setArticleGroup(this);
> }
>
> public void removeArticlePlacement(ArticlePlacement articlePlacement)
> {
> getArticlePlacements().remove(articlePlacement);
> articlePlacement.setArticleGroup(null);
> }
> }
>
>
>> Thanks,
>>
>> -- markus.
>>
>> Greg Ederer wrote:
>>> Markus Fuchs wrote:
>>>> Hi Greg,
>>>>
>>>> Do you get a foreign key violation w/o using @PreRemove callbacks
>>>> in your pojos? Are you using a current glassfish build?
>>>>
>>> Hi Markus,
>>>
>>> Yes. When I em.remove() an ArticlePlacement without the @PreRemove,
>>> I get a PSQLException (I'm using PostgreSQL) with a foreign key
>>> constraint violation message.
>>>
>>> I'm using v2b50.
>>>
>>> Thanks for the reply.
>>>
>>> Cheers,
>>>
>>> Greg
>>>> Thanks,
>>>>
>>>> -- markus.
>>>>
>>>> Greg Ederer wrote:
>>>>> I managed to get this working by adding the following to Article:
>>>>>
>>>>> @PreRemove
>>>>> public void beforeRemove()
>>>>> {
>>>>> List<ArticlePlacement> articlePlacements = getArticlePlacements();
>>>>> for(ArticlePlacement ap : articlePlacements)
>>>>> {
>>>>> ap.setArticle(null);
>>>>> }
>>>>> }
>>>>>
>>>>> And in ArticlePlacement:
>>>>>
>>>>> @PreRemove
>>>>> public void beforeRemove()
>>>>> {
>>>>> getArticleGroup().removeArticlePlacement(this);
>>>>> if(getArticle() != null)
>>>>> {
>>>>> getArticle().removeArticlePlacement(this);
>>>>> }
>>>>> }
>>>>>
>>>>> I have a feeling this is not the best way to handle this (and, I'm
>>>>> guessing that I'll feel like a dummy when I find out the correct
>>>>> way to do this, because it will be pretty obvious).
>>>>>
>>>>> Any advice welcome.
>>>>>
>>>>> Cheers,
>>>>>
>>>>> Greg
>>>>>
>>>>> Greg Ederer wrote:
>>>>>> Oops! Accidentally hit send. Please ignore previous post.
>>>>>>
>>>>>> I have a model containing three entity classes: Article,
>>>>>> ArticleGroup and ArticlePlacement. An ArticleGroup contains zero
>>>>>> or more ArticlePlacement objects. Each ArticlePlacement wraps an
>>>>>> Article (ArticlePlacement also has a start run date and an end
>>>>>> run date, which allows me to run an article in multiple groups
>>>>>> during different periods).
>>>>>>
>>>>>> So, in Article, I have:
>>>>>>
>>>>>> @OneToMany(mappedBy = "article", cascade=CascadeType.ALL)
>>>>>> private List<ArticlePlacement> articlePlacements = new
>>>>>> ArrayList<ArticlePlacement>();
>>>>>>
>>>>>> In ArticleGroup, I have:
>>>>>>
>>>>>> @OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE,
>>>>>> CascadeType.REFRESH})
>>>>>> @OrderBy("position ASC")
>>>>>> private List<ArticlePlacement> articlePlacements = new
>>>>>> ArrayList<ArticlePlacement>();
>>>>>>
>>>>>> And, in ArticlePlacement, I have:
>>>>>>
>>>>>> @ManyToOne
>>>>>> private Article article;
>>>>>>
>>>>>> @ManyToOne
>>>>>> private ArticleGroup articleGroup;
>>>>>>
>>>>>> @PreRemove
>>>>>> public void beforeRemove()
>>>>>> {
>>>>>> getArticleGroup().removeArticlePlacement(this);
>>>>>> getArticle().removeArticlePlacement(this);
>>>>>> }
>>>>>>
>>>>>> When I EntityManager.remove() an Article, I get a
>>>>>> java.util.ConcurrentModificationException due to the
>>>>>> getArticle().removeArticlePlacement(this) in @PreRemove, above.
>>>>>> But, if I comment this out, I get a "violates foreign key"
>>>>>> PSQLException.
>>>>>>
>>>>>> Can someone tell me how to deal with this situation correctly?
>>>>>>
>>>>>> Thanks!
>>>>>>
>>>>>> Greg
>>>>>>
>>>>>
>>>>>
>>>
>>>
>
>


-- 
| E R G O N O S I S
| Greg Ederer
| Lead Developer
| greg_at_ergonosis.com
| 360.774.6848
|