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.