Hi Greg,
I can't see, why you should get a foreign key violation removing an
ArticlePlacement, since ArticlePlacement owns the the relationships to
Article and ArticleGroup. To only other relationship is a
uni-directional OneToMany between Article and ArticleGroup. But neither
Articles nor ArticleGroups are removed, correct?
BTW, in
> utx.begin();
> em.merge(o);
> em.joinTransaction();
> em.remove(o);
> utx.commit();
>
you should call the remove operation on the managed copy returned by
em.merge(o)
Thanks,
-- markus.
> 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
>>>>>>>
>>>>>>
>>>>>>
>>>>
>>>>
>>
>>
>
>