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
|