persistence@glassfish.java.net

Re: Should the foreign key be set automatically when an element is added to a Collection?

From: Tom Ware <tom.ware_at_oracle.com>
Date: Thu, 31 Aug 2006 15:21:36 -0400

Hi Ellen,

  On the surface, having the persistence provider maintain these kinds
of relationships for you seems like a fairly simple thing to do. In
fact, that functionality was available in EJB 2.1. There are some use
cases that are quite difficult for the provider to efficiently
implement. (For instance, having to maintain relationships on a deleted
object involves finding all the references to that object and fixing
them - depending on the way the foreign keys are set up, that can be
quite a task) According to my understanding, it is complex use cases
like that that caused the specification comittee to remove that
functionality.

  Having said that, there may be some benefit in adding some limited
relationship maintenance as a feature to TopLink essentials. It might
be a good feature to request for a future in the GlassFish issue
tracker. The key to implementing it would be to be very specific about
the usecases where it would work.

  The reason there is no exception in the case you mention below is it
is hard for the persistence provider to tell exactly what you intend to
do with those statements. In addition, validating a model like that
could cause some performance costs - we would have to check all
birdirectional relationships to ensure everything matches up correct.
In a large transaction, that could be quite a costly operation.

-Tom

Ellen Kraffmiller wrote:

>Hi,
>I just realized there's a mistake in the code snippet I sent. It should be:
>
>Study studyA = new Study();
>Study studyB = new Study();
>...
>sa.setStudy(studyA)
>studyB.getStudyAuthors.add(sa);
>em.persist(studyB);
>
>Thanks,
>Ellen
>
>Ellen Kraffmiller wrote:
>
>
>
>>Thanks, Tom.
>>So it seems like for every collection in my Entity object model I need
>>to add a special helper method to maintain the relationship,
>>like this:
>>void addStudyAuthor(StudyAuthor sa) {
>> sa.setStudy(this);
>> studyAuthors.add(sa);
>>}
>>Just my two cents, but I don't understand why the persistence layer
>>doesn't handle this. Why would you ever want to add an entity to a
>>Collection, and save it to the database, without setting the correct
>>foreign key relationship? I also noticed then when you do the following:
>>
>>Study studyA = new Study();
>>Study studyB = new Study();
>>...
>>sa.setStudy(studyA)
>>studyA.getStudyAuthors.add(sa);
>>em.persist(studyB);
>>
>>When studyB is persisted, the fact that it has StudyAuthor sa in its
>>StudyAuthors collection is ignored. StudyAuthor sa still points to
>>studyA. It seems like the persist() call should at least throw an
>>Exception, because the object state you are preserving is internally
>>inconsistent.
>>
>>I'm not sure if this is the right mailing list to ask this question,
>>but I'm curious about the reasoning behind this decision in the spec.
>>Thanks,
>>Ellen
>>
>>Tom Ware wrote:
>>
>>
>>
>>>Hi Ellen,
>>>
>>> The JPA specification requires that the application maintain all
>>>relationships.
>>>
>>> In your example, your call to sa.setStudy(study) is the correct way
>>>to maintain that relationship.
>>>
>>>Thanks,
>>>Tom
>>>Ellen Kraffmiller wrote:
>>>
>>>
>>>
>>>>Hi,
>>>>I have two Entities with a OneToMany relationship. When I deploy the
>>>>Entities, the correct foreign key relationship is generated in the
>>>>"many" side of the relationship. (Although the generated schema
>>>>does not define the foreign key column as required.)
>>>>When I add an element to the in the collection in the owning object,
>>>>the foreign key of the owning object is not set in the dependent
>>>>object. Is this the correct behavior?
>>>>
>>>>Here are the details of the two entities:
>>>>
>>>>@Entity
>>>>public class Study {
>>>>@Id
>>>> private Long id;
>>>>@OneToMany (mappedBy="study", cascade={CascadeType.REMOVE})
>>>> private java.util.Collection<StudyAuthor> studyAuthors;
>>>>...getters and setters ...
>>>>}
>>>>
>>>>@Entity
>>>>public class StudyAuthor { @Id
>>>> private Long id;
>>>> private String value;
>>>> @ManyToOne
>>>> private Study study;
>>>>...getters and setters ...
>>>>}
>>>>
>>>>Here is my code which adds a StudyAuthor to the collection in Study
>>>>(I have defined cascade-persist for the persistence unit):
>>>>
>>>> Study study = new Study();
>>>> StudyAuthor sa = new StudyAuthor();
>>>> sa.setValue(getAuthor());
>>>> study.setStudyAuthors(new ArrayList<StudyAuthor>());
>>>> study.getStudyAuthors().add(sa);
>>>> em.persist(study);
>>>>
>>>>The two entities are both saved in the database, but the foreign key
>>>>in studyAuthor is not generated unless I specifically set it
>>>>by adding sa.setStudy(study) before persisting. This seems like
>>>>something the persistence layer should be doing for me.
>>>>
>>>>Thanks,
>>>>Ellen
>>>>
>>>>
>>>>
>>>>