|
Home > Archive > Apache JDO Project > September 2006 > Relationships mapped with mapped-by (long)
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Relationships mapped with mapped-by (long)
|
|
| Craig L Russell 2006-09-05, 7:12 pm |
| | |
| Jörg von Frantzius 2006-09-06, 7:11 am |
| Hello Craig (et al.),
from what I understand of your proposals, you want to mandate *integrity
of bidirectional associations*. Is that correct? That would be a great
new feature!
Next to that, it seems to me that still you always want to silently
remove objects from their relationships when those objects are deleted.
In my opinion, this should only happen if we e.g. have
delete-action="null" on the corresponding foreign key. As that can be
declared only when foreign keys are declared for the association, maybe
we should have something like "delete-action" on the field level, so the
behaviour can easily be specified?
Apart from that, I'd have two questions. For "One-many relationships",
you write:
> If the "one" side of instance A removes instance X (which currently
> refers to A) from its collection, and instance X has not been added to
> a different collection and its reference has not been changed to
> another instance, the spec is silent.
Why is the spec silent here?
My second question is: why do you want to prevent those "conflicting
changes", or why do you see them conflicting at all? Why can't the
second change simply override the first, in both cases?
Thanks for the proposals, regards,
Jörg
Craig L Russell schrieb:
> Javadogs,
>
> We have an issue with regard to the behavior of relationships mapped
> using mapped-by, where there is a bidirectional relationship
> instantiated by a single database artifact, either a foreign key or a
> row in a join table.
>
> The spec currently defines a subset of behavior where relationships
> are updated by the program, and the memory model must be updated to
> reflect the change in the datastore after flush (including flush for
> commit).
>
> The spec doesn't define the behavior when instances are deleted or
> primary keys are updated.
>
> There are interacting metadata tags that affect the way the database
> schema is defined. Column attribute allows-null specifies whether the
> column is nullable or not. Foreign-key attribute delete-action
> specifies how the foreign key is defined in the database. Field
> attribute mapped-by specifies the field on the other side that shares
> a mapping artifact in the database. Field attribute null-value
> specifies the behavior of null values at flush time. Field attribute
> dependent specifies that there is a dependency relationship (if the
> instance is deleted, the instance referred to by the dependent field
> is also deleted).
>
> The proposals below do not add any more metadata concepts, but reuse
> existing concepts. The proposals are intended for incorporation into
> the JDO 2 maintenance release.
>
> Many-many relationships:
>
> These are implemented as join tables, in which each row in the join
> table corresponds to an element (or key or value) in a field on each
> of the two sides. The join table is typically mapped on one side, and
> the field on the other side refers to the field with a mapped-by
> attribute. But the behavior is the same regardless of which side
> defines the mapping.
>
> If either side adds an instance to the map or collection, at flush
> time a new row is added to the join table. The specification requires
> that for consistency, the other side's collection be updated in memory.
>
> If either side removes an instance from the map or collection, at
> flush time the corresponding row is removed from the join table. The
> specification requires that for consistency, the other side's
> collection be updated in memory to remove the corresponding element or
> entry from the collection or map.
>
> If an instance participating in a relationship is deleted, and the
> field referring to the other side is defined as dependent, then all
> instances of the other side are deleted, and all rows in the join
> table corresponding to the deleted instances are deleted.
>
> If an instance participating in a relationship is deleted, and the
> field referring to the other side is not defined as dependent, at
> flush time the corresponding rows are removed from the join table. I
> believe that the specification requires that for consistency, all of
> the other side's collections be updated in memory to remove the
> corresponding element or entry from the collection or map. But I think
> it would be a good idea to add this explicitly to the specification.
>
> One-many relationships:
>
> The foreign key is typically specified on the "many" side, which
> declares a reference type; and the "one" side declares an element,
> key, or value, and uses the mapped-by attribute. The specification
> doesn't require the mapped-by attribute on the "one" side; it's just
> less work to define it there.
>
> These relationships might also be mapped using a join table in which
> the existence of a relationship is indicated by a row in the join
> table. The semantics and behavior are the same as if the column
> defining the relationship is in the "many" side's mapping.
>
> If the "many" side reference in instance X is updated (from instance A
> to instance B) then the spec requires that the underlying database
> column(s) be updated from referring to A to referring to B. The memory
> model is made consistent, by removing X from A's collection and adding
> X to B's collection.
>
> If the "one" side of instance B adds instance X (which currently
> refers to A) to its collection, at flush time the specification
> requires that the underlying database column be updated in the row
> corresponding to instance X to refer to B. The memory model is made
> consistent, by removing X from A's collection and updating X to refer
> to B. A conflicting update is one in which some other instance of the
> "one" type also adds X to its collection, or instance X is changed so
> it refers to an instance of the "one" type other than B.
>
> If the "one" side of instance A removes instance X (which currently
> refers to A) from its collection, and instance X has not been added to
> a different collection and its reference has not been changed to
> another instance, the spec is silent.
>
> <proposed>
> If the field in X is defined as null-value="exception", then at flush
> time this is an error. If the column that defines the relationship is
> defined as allows-null="false", then this is an error, as there is no
> known value to which the column can be changed. If the column is
> defined as allows-null="true" and the field in X is defined as
> null-value="none", then the column is updated to null and the memory
> model is changed so that the reference in X is null.
>
> Deleting an instance X on the "many" side results in removing the
> instance from the database and updating the memory model to be
> consistent. If the field of instance A that contains X is instantiated
> in memory, then the collection is updated to remove X.
>
> Deleting an instance A on the "one" side that contains an instance X
> is ok if instance X is also deleted in the same transaction. There are
> two cases:
>
> o If the collection field is defined as dependent-element="true" then
> instance X is also deleted by the jdo implementation.
>
> o If instance X is not also deleted by the program, and the collection
> field is defined with dependent-element="false" then instance X is not
> deleted. If the field in X is defined as null-value="exception", then
> at flush time this is an error, as the memory model cannot be made
> consistent. If the column that defines the relationship is defined as
> allows-null="false", then this is an error, as there is no known value
> to which the column can be changed. If the column is defined
> as allows-null="true", and the field in X is defined as
> null-value="none", then at flush time the column is updated to null
> and the memory model is changed so that the reference in X is null.
> NOTE: This behavior can be done automatically by the database if the
> foreign-key specifies delete-action="null".
> </proposed>
>
> One-one relationships:
>
> These relationships are symmetric from the object model perspective,
> as currently the only metadata to describe whether one side or the
> other can exist independently is the null-value attribute of field.
> When mapped, typically the mapping is to a single unique foreign key
> column. This column is defined on one side of the relationship, and
> the mapping of the other side uses the mapped-by attribute. For the
> purposes of this discussion, I'll refer to the side containing the
> foreign key in its mapping as the mapped side, and the other as the
> mapped-by side.
>
> If the mapped-by side reference of instance C is updated (from
> instance Y to instance Z) then at flush time the underlying database
> column(s) of Z is updated to refer to C. The specification requires
> that the memory model be made consistent, by changing Z to refer to C.
> Also, since there can only be one value in the database column
> containing C, the row corresponding to Y must be changed, but if Y has
> not also been updated to refer to another instance and no other
> instance has been updated to refer to Y, there is no value to which to
> set Y's reference except null.
>
> <proposed>
> If the field in Y is defined as null-value="exception", this is an
> error. If the field is defined as null-value="none", and the column
> defining the relationship is defined as allows-null="false", this is
> an error. If the field is defined as null-value="none", and the column
> defining the relationship is defined as allows-null="true", then the
> column is updated to null and the memory model is made consistent by
> setting the field in Y to null.
> </proposed>
>
> If the mapped side of instance Y is set to instance D (and Y
> currently refers to C), the spec requires that the underlying database
> column be updated in instance Y to refer to D. The memory model is
> made consistent, by setting the reference in D to Y and setting C's
> reference to null.
>
> <proposed>
> If the field in C is defined as null-value="exception", this is an
> error. If the field is defined as null-value="none", the column in the
> row corresponding to instance Y is updated to refer to D and there is
> no longer any row with a reference to C.
> </proposed>
>
> If the mapped-by side reference of instance C is deleted (and
> currently instance C refers to Y and vice versa) and the field in C is
> defined as dependent then at flush time both C and Y are deleted.
>
> If the field in C is not defined as dependent and Y has not been
> changed to refer to another instance, then at flush time the reference
> in Y to C must be set to null.
>
> <proposed>
> If the field in Y is defined as null-value="exception", this is an
> error. If the field is defined as null-value="none", and the column
> defining the relationship is defined as allows-null="false", this is
> an error. If the field is defined as null-value="none", and the column
> defining the relationship is defined as allows-null="true", then the
> column in Y is updated to null and the memory model is made consistent
> by setting the field in Y to null.
> </proposed>
>
> If the mapped side reference of instance Y is deleted (and currently
> instance Y refers to C and vice versa) and the field in Y that refers
> to C is defined as dependent, then at flush time the rows
> corresponding to Y and C are deleted.
>
> If the field is not defined as dependent, then at flush time the field
> in C must be set to null.
>
> <proposed>
> If the field in C is defined as null-value="exception", this is an
> error. If the field is defined as null-value="none", the row
> corresponding to instance Y is deleted and there is no instance that
> refers to C.
> </proposed>
>
> <spec>
> The field on the other side of the relationship can be mapped by using
> the mapped-by at-
> tribute identifying the field on the side that defines the mapping.
> Regardless of which side
> changes the relationship, flush (whether done as part of commit or
> explicitly by the user)
> will modify the datastore to reflect the change and will update the
> memory model for con-
> sistency. There is no further behavior implied by having both sides of
> the relationship map
> to the same database column(s). In particular, making a change to one
> side of the relation-
> ship does not imply any runtime behavior by the JDO implementation to
> change the other
> side of the relationship in memory prior to flush, and there is no
> requirement to load fields
> affected by the change if they are not already loaded. This implies
> that if the RetainVal-
> ues flag or DetachAllOnCommit is set to true, and the relationship
> field is loaded, then
> the implementation will change the field on the other side so it is
> visible after transaction
> completion.
> Conflicting changes to relationships cause a JDOUserException to be
> thrown at flush
> time. Conflicting changes include:
> •adding a related instance with a single-valued mapped-by relationship
> field to
> more than one one-to-many collection relationship
> •setting both sides of a one-to-one relationship such that they do not
> refer to each
> other
> </spec>
>
>
> Craig Russell
>
> Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
>
> 408 276-5638 mailto:Craig.Russell@sun.com
>
> P.S. A good JDO? O, Gasp!
>
>
| |
| Jörg von Frantzius 2006-09-06, 7:11 am |
| Jörg von Frantzius schrieb:
[delete-action="null"]
> As that can be declared only when foreign keys are declared for the
> association, maybe we should have something like "delete-action" on
> the field level, so the behaviour can easily be specified?
I just learned that delete-action also exists on field level as a
shortcut, so please ignore that sentence...
| |
| Matthew Adams 2006-09-08, 7:11 pm |
| The scenario that we discussed on the phone for which I had an action
item is already addressed in the specification, section 15.3, paragraph
6, bullet point 2:
"Conflicting changes include...setting both sides of a one-to-one
relationship such that they do not refer to each other"
No spec update is required to handle the scenario where E is updated to
refer to V then V is updated to refer to something other than E.
--matthew
________________________________
From: Craig.Russell@Sun.COM [mailto:Craig.Russell@Sun.COM]
Sent: Tuesday, September 05, 2006 1:29 PM
To: JDO Expert Group; Apache JDO project
Subject: Relationships mapped with mapped-by (long)
Javadogs,
We have an issue with regard to the behavior of relationships
mapped using mapped-by, where there is a bidirectional relationship
instantiated by a single database artifact, either a foreign key or a
row in a join table.
The spec currently defines a subset of behavior where
relationships are updated by the program, and the memory model must be
updated to reflect the change in the datastore after flush (including
flush for commit).
The spec doesn't define the behavior when instances are deleted
or primary keys are updated.
There are interacting metadata tags that affect the way the
database schema is defined. Column attribute allows-null specifies
whether the column is nullable or not. Foreign-key attribute
delete-action specifies how the foreign key is defined in the database.
Field attribute mapped-by specifies the field on the other side that
shares a mapping artifact in the database. Field attribute null-value
specifies the behavior of null values at flush time. Field attribute
dependent specifies that there is a dependency relationship (if the
instance is deleted, the instance referred to by the dependent field is
also deleted).
The proposals below do not add any more metadata concepts, but
reuse existing concepts. The proposals are intended for incorporation
into the JDO 2 maintenance release.
Many-many relationships:
These are implemented as join tables, in which each row in the
join table corresponds to an element (or key or value) in a field on
each of the two sides. The join table is typically mapped on one side,
and the field on the other side refers to the field with a mapped-by
attribute. But the behavior is the same regardless of which side defines
the mapping.
If either side adds an instance to the map or collection, at
flush time a new row is added to the join table. The specification
requires that for consistency, the other side's collection be updated in
memory.
If either side removes an instance from the map or collection,
at flush time the corresponding row is removed from the join table. The
specification requires that for consistency, the other side's collection
be updated in memory to remove the corresponding element or entry from
the collection or map.
If an instance participating in a relationship is deleted, and
the field referring to the other side is defined as dependent, then all
instances of the other side are deleted, and all rows in the join table
corresponding to the deleted instances are deleted.
If an instance participating in a relationship is deleted, and
the field referring to the other side is not defined as dependent, at
flush time the corresponding rows are removed from the join table. I
believe that the specification requires that for consistency, all of the
other side's collections be updated in memory to remove the
corresponding element or entry from the collection or map. But I think
it would be a good idea to add this explicitly to the specification.
One-many relationships:
The foreign key is typically specified on the "many" side, which
declares a reference type; and the "one" side declares an element, key,
or value, and uses the mapped-by attribute. The specification doesn't
require the mapped-by attribute on the "one" side; it's just less work
to define it there.
These relationships might also be mapped using a join table in
which the existence of a relationship is indicated by a row in the join
table. The semantics and behavior are the same as if the column defining
the relationship is in the "many" side's mapping.
If the "many" side reference in instance X is updated (from
instance A to instance B) then the spec requires that the underlying
database column(s) be updated from referring to A to referring to B. The
memory model is made consistent, by removing X from A's collection and
adding X to B's collection.
If the "one" side of instance B adds instance X (which currently
refers to A) to its collection, at flush time the specification requires
that the underlying database column be updated in the row corresponding
to instance X to refer to B. The memory model is made consistent, by
removing X from A's collection and updating X to refer to B. A
conflicting update is one in which some other instance of the "one" type
also adds X to its collection, or instance X is changed so it refers to
an instance of the "one" type other than B.
If the "one" side of instance A removes instance X (which
currently refers to A) from its collection, and instance X has not been
added to a different collection and its reference has not been changed
to another instance, the spec is silent.
<proposed>
If the field in X is defined as null-value="exception", then at
flush time this is an error. If the column that defines the relationship
is defined as allows-null="false", then this is an error, as there is no
known value to which the column can be changed. If the column is defined
as allows-null="true" and the field in X is defined as
null-value="none", then the column is updated to null and the memory
model is changed so that the reference in X is null.
Deleting an instance X on the "many" side results in removing
the instance from the database and updating the memory model to be
consistent. If the field of instance A that contains X is instantiated
in memory, then the collection is updated to remove X.
Deleting an instance A on the "one" side that contains an
instance X is ok if instance X is also deleted in the same transaction.
There are two cases:
o If the collection field is defined as dependent-element="true"
then instance X is also deleted by the jdo implementation.
o If instance X is not also deleted by the program, and the
collection field is defined with dependent-element="false" then instance
X is not deleted. If the field in X is defined as
null-value="exception", then at flush time this is an error, as the
memory model cannot be made consistent. If the column that defines the
relationship is defined as allows-null="false", then this is an error,
as there is no known value to which the column can be changed. If the
column is defined as allows-null="true", and the field in X is defined
as null-value="none", then at flush time the column is updated to null
and the memory model is changed so that the reference in X is null.
NOTE: This behavior can be done automatically by the database if the
foreign-key specifies delete-action="null".
</proposed>
One-one relationships:
These relationships are symmetric from the object model
perspective, as currently the only metadata to describe whether one side
or the other can exist independently is the null-value attribute of
field. When mapped, typically the mapping is to a single unique foreign
key column. This column is defined on one side of the relationship, and
the mapping of the other side uses the mapped-by attribute. For the
purposes of this discussion, I'll refer to the side containing the
foreign key in its mapping as the mapped side, and the other as the
mapped-by side.
If the mapped-by side reference of instance C is updated (from
instance Y to instance Z) then at flush time the underlying database
column(s) of Z is updated to refer to C. The specification requires that
the memory model be made consistent, by changing Z to refer to C. Also,
since there can only be one value in the database column containing C,
the row corresponding to Y must be changed, but if Y has not also been
updated to refer to another instance and no other instance has been
updated to refer to Y, there is no value to which to set Y's reference
except null.
<proposed>
If the field in Y is defined as null-value="exception", this is
an error. If the field is defined as null-value="none", and the column
defining the relationship is defined as allows-null="false", this is an
error. If the field is defined as null-value="none", and the column
defining the relationship is defined as allows-null="true", then the
column is updated to null and the memory model is made consistent by
setting the field in Y to null.
</proposed>
If the mapped side of instance Y is set to instance D (and Y
currently refers to C), the spec requires that the underlying database
column be updated in instance Y to refer to D. The memory model is made
consistent, by setting the reference in D to Y and setting C's reference
to null.
<proposed>
If the field in C is defined as null-value="exception", this is
an error. If the field is defined as null-value="none", the column in
the row corresponding to instance Y is updated to refer to D and there
is no longer any row with a reference to C.
</proposed>
If the mapped-by side reference of instance C is deleted (and
currently instance C refers to Y and vice versa) and the field in C is
defined as dependent then at flush time both C and Y are deleted.
If the field in C is not defined as dependent and Y has not been
changed to refer to another instance, then at flush time the reference
in Y to C must be set to null.
<proposed>
If the field in Y is defined as null-value="exception", this is
an error. If the field is defined as null-value="none", and the column
defining the relationship is defined as allows-null="false", this is an
error. If the field is defined as null-value="none", and the column
defining the relationship is defined as allows-null="true", then the
column in Y is updated to null and the memory model is made consistent
by setting the field in Y to null.
</proposed>
If the mapped side reference of instance Y is deleted (and
currently instance Y refers to C and vice versa) and the field in Y that
refers to C is defined as dependent, then at flush time the rows
corresponding to Y and C are deleted.
If the field is not defined as dependent, then at flush time the
field in C must be set to null.
<proposed>
If the field in C is defined as null-value="exception", this is
an error. If the field is defined as null-value="none", the row
corresponding to instance Y is deleted and there is no instance that
refers to C.
</proposed>
<spec>
The field on the other side of the relationship can be mapped by
using the mapped-by at-
tribute identifying the field on the side that defines the
mapping. Regardless of which side
changes the relationship, flush (whether done as part of commit
or explicitly by the user)
will modify the datastore to reflect the change and will update
the memory model for con-
sistency. There is no further behavior implied by having both
sides of the relationship map
to the same database column(s). In particular, making a change
to one side of the relation-
ship does not imply any runtime behavior by the JDO
implementation to change the other
side of the relationship in memory prior to flush, and there is
no requirement to load fields
affected by the change if they are not already loaded. This
implies that if the RetainVal-
ues flag or DetachAllOnCommit is set to true, and the
relationship field is loaded, then
the implementation will change the field on the other side so it
is visible after transaction
completion.
Conflicting changes to relationships cause a JDOUserException to
be thrown at flush
time. Conflicting changes include:
*adding a related instance with a single-valued mapped-by
relationship field to
more than one one-to-many collection relationship
*setting both sides of a one-to-one relationship such that they
do not refer to each
other
</spec>
Craig Russell
Architect, Sun Java Enterprise System
http://java.sun.com/products/jdo
408 276-5638 mailto:Craig.Russell@sun.com
P.S. A good JDO? O, Gasp!
| |
| David Bullock 2006-09-09, 1:11 pm |
| On 9/6/06, Craig L Russell <Craig.Russell@sun.com> wrote:
>
>
> We have an issue with regard to the behavior of relationships mapped using
> mapped-by, where there is a bidirectional relationship instantiated by a
> single database artifact, either a foreign key or a row in a join table.
>
> The spec currently defines a subset of behavior where relationships are
> updated by the program, and the memory model must be updated to reflect the
> change in the datastore after flush (including flush for commit).
>
You already know that as a user of JDO, I view this behaviour as obnoxious -
the JDO implementation should not alter the semantic state of my objects,
and it should by no means attempt to guess my intent when I have made
ambiguous alterations (an exception telling me to get my act together is all
I need). Interesting that this EJB3-envy feature is introducing new
issues. I would much prefer that you dropped it and tackled the issues of
relationship-management comprehensively in a separate JSR, which used to be
your entirely logical stance on the matter. I regard the need for
maintenance in this area as informal evidence of an iceberg waiting beneath
the tip.
sincerely,
David Bullock.
| |
| Craig L Russell 2006-09-09, 7:11 pm |
| |
|
|
|
|