Only on specific scenario - "all-delete-orphan" was no longe
oberiko Jan 24, 2008 4:15 PMHello.
I have a demo app where the user can enter people and save them to a database. Each person can have 0..* email addresses. The user can also go to a seperate page where the people are listed and select one to edit (taking them back to the first screen, now pre-populated with the person data)
The following use cases work just fine:
* Save new person with and without emails
* Edit person with emails
* Edit person without emails, who formerly had emails
* Edit person (who had an email added, and removed before being saved)
* Save new person without email, go to list page, select the person, then edit them
* Etc.
What's odd is that in one scenario, it doesn't work. That's if I create a new person, save them (without adding any emails), then modify them and save again (still without adding any emails). If, at any point, I add an email, even without saving it, it works.
The error I get when this does happen is as such:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance
Here's the relevant snippets of my code:
@Stateful
@Name("editPerson")
public class EditPersonForm implements EditPersonLocal {
@PersistenceContext(type = EXTENDED)
private EntityManager em;
@In(required = false)
@Out(scope = ScopeType.PAGE)
private Person person;
@In(required = false)
@Out(required = false)
private EmailAddress emailAddress;
public void addEmail() {
log.info("Adding email address #0", emailAddress.getUrl());
person.addEmailAddress(emailAddress);
//Blank the email address entry point
emailAddress = new EmailAddress();
}
public void removeEmail(EmailAddress emailAddress) {
log.info("Removing email address #0", emailAddress.getUrl());
person.removeEmailAddress(emailAddress);
}
public void savePerson() {
if (person.getId() != null){
em.merge(person);
}
else {
em.persist(person);
}
// Note that the id of our person was generated and populated.
facesMessages.add("#0 was saved with an id of #1", person.getName(), person.getId());
log.info(person.getName() + " was saved.");
}
}
Name:
<h:inputText value="#{person.name}" required="true"
requiredMessage="You need to enter a name" />
<h:outputText value="#{person.id}" />
<br />
<hr />
Emails:<br />
URL: <h:inputText value="#{emailAddress.url}" />
<h:commandButton action="#{editPerson.addEmail()}" value="Add email" />
<h:dataTable id="emailTable" value="#{person.emailAddresses}"
var="email" rendered="#{not empty person.emailAddresses}">
<h:column>
<f:facet name="header">
<h:outputText value="URL" />
</f:facet>
<h:outputText value="#{email.url}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Action" />
</f:facet>
<h:commandButton value="Remove"
action="#{editPerson.removeEmail(email)}" />
</h:column>
</h:dataTable>
<hr />
<h:commandButton value="Save" action="#{editPerson.savePerson()}" />
<h:commandButton value="New" action="#{editPerson.newPerson()}" />
@Entity
@Name("person")
public class Person {
@Id @GeneratedValue
private Long id;
@NotNull
private String name;
@OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="person")
@org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Collection<EmailAddress> emailAddresses;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Collection<EmailAddress> getEmailAddresses() {
return emailAddresses;
}
public void setEmailAddresses(Collection<EmailAddress> emailAddresses) {
this.emailAddresses = emailAddresses;
}
public void addEmailAddress(EmailAddress email){
if (emailAddresses == null) emailAddresses= new ArrayList<EmailAddress>();
email.setPerson(this);
emailAddresses.add(email);
}
public void removeEmailAddress(EmailAddress emailAddress){
if (emailAddresses != null){
emailAddresses.remove(emailAddress);
}
}
}
@Entity
@Name("emailAddress")
public class EmailAddress {
@Id @GeneratedValue
private Long id;
@NotNull
private String url;
@ManyToOne
@JoinColumn(name="person_id")
private Person person;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
Now, I have found that if I put the following code right before the "em.merge(person)" (in the top code block), I no longer have this issue and it works.
if (person.getEmailAddresses() == null) person.setEmailAddresses(new ArrayList<EmailAddress>());
Is this a known issue? Oh, and I apologize if this is isn't the correct forum for this, I wasn't sure if it was more EJB or Hibernate related.