After much more discussion about self-referencing foreign
keys and how it should be implemented, here is what I came
up with. The getParent, getChildren methods would not be
appropriate because the nature of what we are discussing is
modeling a general Directed Graph.
The nature of the self-referencing relationships are not
necessarily that of Parent and Children. Also, there can be
cases where there are multiple self-referencing foreign keys
in the same table. For that situation the code would break
because the template would generate multiple getParent,
getChildren methods.
The "RelatedBy" methods do not have this multiple foreign
key issues because they include the column name that defines
the foreign key relationship. I think for the situation where
the local table and foreign table map to different object types,
the "RelatedBy" methods are fairly clear. For example suppose we
have two tables FOO and BAR that map to objects Foo and Bar and
Foo has a foreign key A_BAR_ID that maps to BAR's BAR_ID.
The Foo object has the method
getBarRelatedByABarId()
In this case you expect it to return the object of type Bar
where "this" Foo's A_BAR_ID matches the Bar's BAR_ID.
The Bar object has the method
getFoosRelatedByABarId()
Which would return a List of all the Foo objects where
"this" Bar object's BAR_ID matches a the Foos' A_BAR_ID.
Some confusion could occur for the objects with self-referencing
foreign keys. In the case of an table FOO for object Foo which
has a foreign key ANOTHER_FOO_ID that maps to another Foo's FOO_ID.
You would get the methods
getFooRelatedByAnotherFooId()
and
getFoosRelatedByAnotherFooId()
I think the first method is still fairly clear but the second
is kind of confusing. In the current template the first method
is being generated for self-referencing foreign keys but the
second is not. Since it is not currently being generated I
propose we make the method name more explicit. The scheme I
came up with is
get<Object_Type>sWhose<Foreign_Key_Java_Name)IsThis(Primary_Key_Java_Name).
So for my Foo example this would be
getFoosWhoseAnotherFooIdIsThisFooId()
This explicitly states the foreign key relationship so there
is less confusion. I am not fond of the method names but I
was not clever enough to come up with a better one that explicity
states the object relationships. I would assume that people
would add wrapper methods to make the relationships clearer.
For my case where I have Folder object that has a foreign key
reference to a parent folder I would wrap the method
getFoldersWhoseParentIDIsThisFolderID()
with the method
getChildren()
to make it easier to use. If anyone has a better naming scheme
let me know and I will use it.
A patch for the Object.vm template is attached and included below.
-- Steve Davis
========================= Begin Patch ============================
Index: src/templates/om/Object.vm
===================================================================
RCS file: /home/cvspublic/jakarta-turbine-torque/src/templates/om/Object.vm,v
retrieving revision 1.33
diff -u -r1.33 Object.vm
--- src/templates/om/Object.vm 13 Jun 2002 18:25:23 -0000 1.33
+++ src/templates/om/Object.vm 20 Jun 2002 16:44:21 -0000
@@ -161,18 +161,27 @@
#set ( $varName = "a$tblFK.JavaName" )
#end
+ // $varName references the $tblFK.JavaName pointed to by the
+ // foreign key $col.JavaName.
if ($varName != null && !Objects.equals(${varName}.get${colFK.JavaName}(), v))
{
$varName = null;
}
#end
+
#foreach ($fk in $col.Referrers)
#set ( $fkColName = $fk.ForeignLocalMapping.get($col.Name) )
#set ( $tblFK = $fk.Table )
- #if ( !($tblFK.Name.equals($table.Name)) )
#set ( $colFK = $tblFK.getColumn($fkColName) )
- #if ($colFK.isMultipleFK())
+
+ ## Check to see if this is a self-referencing foreign key
+ ## If it is, then the name of the collection of objects is
+ ## different to be more explicit about the relationship.
+ #if ( $tblFK.Name.equals($table.Name) )
+ #set ( $relatedToCol = $tblFK.getColumn(${col.Name}).JavaName )
+ #set ( $collName =
+"coll${tblFK.JavaName}sWhose${colFK.JavaName}IsThis${relatedToCol}" )
+ #elseif ($colFK.isMultipleFK())
#set ( $collName = "coll${tblFK.JavaName}sRelatedBy$colFK.JavaName" )
#else
#set ( $collName = "coll${tblFK.JavaName}s" )
@@ -187,7 +196,6 @@
.set${colFK.JavaName}(v);
}
}
- #end
#end
#end
}
@@ -332,13 +340,27 @@
##
#foreach ($fk in $table.Referrers)
#set ( $tblFK = $fk.Table )
- #if ( !($tblFK.Name.equals($table.Name)) )
#set ( $className = $tblFK.JavaName )
#set ( $relatedByCol = "" )
+
#foreach ($columnName in $fk.LocalColumns)
#set ( $column = $tblFK.getColumn($columnName) )
- #if ($column.isMultipleFK())
- #set ($relatedByCol= "$relatedByCol$column.JavaName")
+ #if ($column.isMultipleFK() || $tblFK.Name.equals($table.Name) )
+ #set ($relatedByCol= "$relatedByCol$column.JavaName")
+ #if ( $tblFK.Name.equals($table.Name) )
+
+ ## This means we have a self-referencing foreign key. We
+ ## will need to build more explicit method names so get
+ ## "related to" column in addition to the "related by" column.
+
+ #set ( $colFKName = $fk.LocalForeignMapping.get($columnName) )
+
+ ## This will get the "foreign" table column name but since
+ ## the foreign and local table names are the same we can
+ ## still get the column from this table
+
+ #set ( $relatedToCol = $tblFK.getColumn( $colFKName ).JavaName )
+ #end
#end
#end
@@ -347,9 +369,19 @@
#set ( $relCol = "${className}s" )
#set ( $relColMs = $className )
#else
- #set ( $suffix = "RelatedBy$relatedByCol" )
- #set ( $relCol= "${className}sRelatedBy$relatedByCol" )
- #set ( $relColMs= "${className}RelatedBy$relatedByCol" )
+ #if ( $tblFK.Name.equals($table.Name) )
+ #set ( $suffix = "RelatedBy$relatedByCol" )
+
+ ## If this is a self-referencing foreign key then make the
+ ## relationship identifier more explicit for clarity.
+
+ #set ( $relCol= "${className}sWhose${relatedByCol}IsThis${relatedToCol}" )
+ #set ( $relColMs= "${className}Whose${relatedByCol}IsThis${relatedToCol}" )
+ #else
+ #set ( $suffix = "RelatedBy$relatedByCol" )
+ #set ( $relCol= "${className}sRelatedBy$relatedByCol" )
+ #set ( $relColMs= "${className}RelatedBy$relatedByCol" )
+ #end
#end
#set ( $collName = "coll$relCol" )
@@ -569,7 +601,7 @@
return $collName;
}
- #end #end #end #end
+ #end #end #end
## ===========================================================
#*
@@ -911,23 +943,43 @@
#if ($complexObjectModel)
#foreach ($fk in $table.Referrers)
#set ( $tblFK = $fk.Table )
- #if ( !($tblFK.Name.equals($table.Name)) )
#set ( $className = $tblFK.JavaName )
#set ( $relCol = "" )
+ #set ( $relatedToCol = "" )
#foreach ($columnName in $fk.LocalColumns)
#set ( $column = $tblFK.getColumn($columnName) )
- #if ($column.isMultipleFK())
- #set ( $relCol = "$relCol$column.JavaName" )
+ #if ($column.isMultipleFK() || $tblFK.Name.equals($table.Name) )
+ #if ( $tblFK.Name.equals($table.Name) )
+ ## This means we have a self-referencing foreign key. We
+ ## will need to build more explicit method names so get
+ ## "related to" column in addition to the "related by" column.
+
+ #set ( $colFKName = $fk.LocalForeignMapping.get($columnName) )
+
+ ## This will get the "foreign" table column name but since
+ ## the foreign and local table names are the same we can
+ ## still get the column from this table.
+
+ #set ( $relatedToCol = $tblFK.getColumn( $colFKName ).JavaName )
+
+ #end
+ #set ( $relCol = "$relCol$column.JavaName" )
#end
#end
+
#if ($relCol == "")
#set ( $relCol = "${className}s" )
#else
+ #if ( $tblFK.Name.equals($table.Name) )
+ #set ( $relCol = "${className}sWhose${relCol}IsThis${relatedToCol}" )
+ #else
#set ( $relCol = "${className}sRelatedBy$relCol" )
+ #end
#end
#set ( $collName = "coll$relCol" )
+
if ($collName != null)
{
for (int i = 0; i < ${collName}.size(); i++)
@@ -935,7 +987,6 @@
((${className}) ${collName}.get(i)).save(con);
}
}
- #end
#end
#end
#if ($complexObjectModel)
@@ -1139,13 +1190,27 @@
#set ( $list = "List " )
#foreach ($fk in $table.Referrers)
#set ( $tblFK = $fk.Table )
- #if ( !($tblFK.Name.equals($table.Name)) )
#set ( $className = $tblFK.JavaName )
#set ( $relCol = "" )
+ #set ( $relatedToCol = "" )
#foreach ($columnName in $fk.LocalColumns)
#set ( $column = $tblFK.getColumn($columnName) )
- #if ($column.isMultipleFK())
- #set ( $relCol = "$relCol$column.JavaName" )
+ #if ($column.isMultipleFK() || $tblFK.Name.equals($table.Name) )
+ #if ( $tblFK.Name.equals($table.Name) )
+ ## This means we have a self-referencing foreign key. We
+ ## will need to build more explicit method names so get
+ ## "related to" column in addition to the "related by" column.
+
+ #set ( $colFKName = $fk.LocalForeignMapping.get($columnName) )
+
+ ## This will get the "foreign" table column name but since
+ ## the foreign and local table names are the same we can
+ ## still get the column from this table
+
+ #set ( $relatedToCol = $tblFK.getColumn( $colFKName ).JavaName )
+
+ #end
+ #set ( $relCol = "$relCol$column.JavaName" )
#end
#end
@@ -1153,8 +1218,13 @@
#set ( $pCollName = "${className}s" )
#set ( $pCollNameNoS = "${className}" )
#else
- #set ( $pCollName = "${className}sRelatedBy$relCol" )
- #set ( $pCollNameNoS = "${className}RelatedBy$relCol" )
+ #if ( $tblFK.Name.equals($table.Name) )
+ #set ( $pCollName = "${className}sWhose${relCol}IsThis${relatedToCol}" )
+ #set ( $pCollNameNoS =
+"${className}Whose${relCol}IsThis${relatedToCol}" )
+ #else
+ #set ( $pCollName = "${className}sRelatedBy$relCol" )
+ #set ( $pCollNameNoS = "${className}RelatedBy$relCol" )
+ #end
#end
${list}v = get${pCollName}();
@@ -1165,7 +1235,6 @@
((Persistent) v.get(i)).setNew(true);
}
#set ( $list = "" )
- #end
#end
copyObj.setNew(true);
#end
========================= End Patch ============================
-----Original Message-----
From: Scott Finnerty
Sent: Friday, June 14, 2002 9:14 AM
To: Turbine Torque Developers List
Subject: RE: [PATCH] Support recursive foreign key relationships
Thanks for the discussion - we considered the comments and suggestions and
came up with the following:
1) Create, in the case of a self-referring foreign key only, convenience methods
that are named less ambiguously - we suggest getParent and
getChildren. We are working on the necessary template modification.
2) The naming convention "RelatedBy" is used for clarity and to avoid method
name-collision when a particular column participates in more than one
foreign key. Its use on self-referring foreign keys is not technically
necessary or consistent. We perpetuated the use of RelatedBy because it was
already used in the template when generating the additional accessors for
an attribute that is also a foreign key in other tables. (The patch we
submitted originally added the accessor for the foreign key when it was
self-referring - i.e., the get children method.)
So it seems there is a choice:
a) Remove the use of "RelatedBy" for self-referring foreign keys - this would
change method names as they have previously existed. We will mitigate the
impact by continuing to generate the "RelatedBy" form of the methods and
deprecate them.
- or -
b) Continue the use of RelatedBy for self-referring foreign keys.
If everyone/anyone will indicate their preference, we'll submit an appropriate
patch.
Thanks
Scott Finnerty
-----Original Message-----
From: John McNally [mailto:[EMAIL PROTECTED]]
Sent: Thursday, June 13, 2002 3:36 PM
To: Turbine Torque Developers List
Subject: RE: [PATCH] Support recursive foreign key relationships
On Thu, 2002-06-13 at 13:14, Stephen Haberman wrote:
> > The table has a self-referential key to set up parent/child
> > relationships. The method getScarabModulesRelatedByParentId is a bit
> > ambiguous. When writing the template I was thinking a method named
> like
> > this should return results similar to a method named getParentModules.
> > What it will return is ChildrenModules. This is the reason for not
> > generating these methods. If others do not find this confusing, I am
> ok
> > with the patch.
>
> I agree that it's not the clearest thing in the world, but I would hope
> users would get it after a few tests, even if they don't at first.
>
> Given that you already trap for this parent/child foreign key reference
> situation, could you implement getParentModules/getChildModules instead
> of the ambiguous getModulesRelatedByParentId? I'm not familiar enough
> with the template to know whether it's possible or not and could be done
> generally for all (most) situations.
>
I think the method internals could be done generally. I'm not sure how
easy it is to generate a suitable method name generally. The other
method (getParentModules) will require using aliases which had not been
added to criteria when i first coded the templates, but it has since
been added.
john
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>
Index: src/templates/om/Object.vm
===================================================================
RCS file: /home/cvspublic/jakarta-turbine-torque/src/templates/om/Object.vm,v
retrieving revision 1.33
diff -u -r1.33 Object.vm
--- src/templates/om/Object.vm 13 Jun 2002 18:25:23 -0000 1.33
+++ src/templates/om/Object.vm 20 Jun 2002 16:44:21 -0000
@@ -161,18 +161,27 @@
#set ( $varName = "a$tblFK.JavaName" )
#end
+ // $varName references the $tblFK.JavaName pointed to by the
+ // foreign key $col.JavaName.
if ($varName != null && !Objects.equals(${varName}.get${colFK.JavaName}(), v))
{
$varName = null;
}
#end
+
#foreach ($fk in $col.Referrers)
#set ( $fkColName = $fk.ForeignLocalMapping.get($col.Name) )
#set ( $tblFK = $fk.Table )
- #if ( !($tblFK.Name.equals($table.Name)) )
#set ( $colFK = $tblFK.getColumn($fkColName) )
- #if ($colFK.isMultipleFK())
+
+ ## Check to see if this is a self-referencing foreign key
+ ## If it is, then the name of the collection of objects is
+ ## different to be more explicit about the relationship.
+ #if ( $tblFK.Name.equals($table.Name) )
+ #set ( $relatedToCol = $tblFK.getColumn(${col.Name}).JavaName )
+ #set ( $collName =
+"coll${tblFK.JavaName}sWhose${colFK.JavaName}IsThis${relatedToCol}" )
+ #elseif ($colFK.isMultipleFK())
#set ( $collName = "coll${tblFK.JavaName}sRelatedBy$colFK.JavaName" )
#else
#set ( $collName = "coll${tblFK.JavaName}s" )
@@ -187,7 +196,6 @@
.set${colFK.JavaName}(v);
}
}
- #end
#end
#end
}
@@ -332,13 +340,27 @@
##
#foreach ($fk in $table.Referrers)
#set ( $tblFK = $fk.Table )
- #if ( !($tblFK.Name.equals($table.Name)) )
#set ( $className = $tblFK.JavaName )
#set ( $relatedByCol = "" )
+
#foreach ($columnName in $fk.LocalColumns)
#set ( $column = $tblFK.getColumn($columnName) )
- #if ($column.isMultipleFK())
- #set ($relatedByCol= "$relatedByCol$column.JavaName")
+ #if ($column.isMultipleFK() || $tblFK.Name.equals($table.Name) )
+ #set ($relatedByCol= "$relatedByCol$column.JavaName")
+ #if ( $tblFK.Name.equals($table.Name) )
+
+ ## This means we have a self-referencing foreign key. We
+ ## will need to build more explicit method names so get
+ ## "related to" column in addition to the "related by" column.
+
+ #set ( $colFKName = $fk.LocalForeignMapping.get($columnName) )
+
+ ## This will get the "foreign" table column name but since
+ ## the foreign and local table names are the same we can
+ ## still get the column from this table
+
+ #set ( $relatedToCol = $tblFK.getColumn( $colFKName ).JavaName )
+ #end
#end
#end
@@ -347,9 +369,19 @@
#set ( $relCol = "${className}s" )
#set ( $relColMs = $className )
#else
- #set ( $suffix = "RelatedBy$relatedByCol" )
- #set ( $relCol= "${className}sRelatedBy$relatedByCol" )
- #set ( $relColMs= "${className}RelatedBy$relatedByCol" )
+ #if ( $tblFK.Name.equals($table.Name) )
+ #set ( $suffix = "RelatedBy$relatedByCol" )
+
+ ## If this is a self-referencing foreign key then make the
+ ## relationship identifier more explicit for clarity.
+
+ #set ( $relCol= "${className}sWhose${relatedByCol}IsThis${relatedToCol}" )
+ #set ( $relColMs= "${className}Whose${relatedByCol}IsThis${relatedToCol}" )
+ #else
+ #set ( $suffix = "RelatedBy$relatedByCol" )
+ #set ( $relCol= "${className}sRelatedBy$relatedByCol" )
+ #set ( $relColMs= "${className}RelatedBy$relatedByCol" )
+ #end
#end
#set ( $collName = "coll$relCol" )
@@ -569,7 +601,7 @@
return $collName;
}
- #end #end #end #end
+ #end #end #end
## ===========================================================
#*
@@ -911,23 +943,43 @@
#if ($complexObjectModel)
#foreach ($fk in $table.Referrers)
#set ( $tblFK = $fk.Table )
- #if ( !($tblFK.Name.equals($table.Name)) )
#set ( $className = $tblFK.JavaName )
#set ( $relCol = "" )
+ #set ( $relatedToCol = "" )
#foreach ($columnName in $fk.LocalColumns)
#set ( $column = $tblFK.getColumn($columnName) )
- #if ($column.isMultipleFK())
- #set ( $relCol = "$relCol$column.JavaName" )
+ #if ($column.isMultipleFK() || $tblFK.Name.equals($table.Name) )
+ #if ( $tblFK.Name.equals($table.Name) )
+ ## This means we have a self-referencing foreign key. We
+ ## will need to build more explicit method names so get
+ ## "related to" column in addition to the "related by" column.
+
+ #set ( $colFKName = $fk.LocalForeignMapping.get($columnName) )
+
+ ## This will get the "foreign" table column name but since
+ ## the foreign and local table names are the same we can
+ ## still get the column from this table.
+
+ #set ( $relatedToCol = $tblFK.getColumn( $colFKName ).JavaName )
+
+ #end
+ #set ( $relCol = "$relCol$column.JavaName" )
#end
#end
+
#if ($relCol == "")
#set ( $relCol = "${className}s" )
#else
+ #if ( $tblFK.Name.equals($table.Name) )
+ #set ( $relCol = "${className}sWhose${relCol}IsThis${relatedToCol}" )
+ #else
#set ( $relCol = "${className}sRelatedBy$relCol" )
+ #end
#end
#set ( $collName = "coll$relCol" )
+
if ($collName != null)
{
for (int i = 0; i < ${collName}.size(); i++)
@@ -935,7 +987,6 @@
((${className}) ${collName}.get(i)).save(con);
}
}
- #end
#end
#end
#if ($complexObjectModel)
@@ -1139,13 +1190,27 @@
#set ( $list = "List " )
#foreach ($fk in $table.Referrers)
#set ( $tblFK = $fk.Table )
- #if ( !($tblFK.Name.equals($table.Name)) )
#set ( $className = $tblFK.JavaName )
#set ( $relCol = "" )
+ #set ( $relatedToCol = "" )
#foreach ($columnName in $fk.LocalColumns)
#set ( $column = $tblFK.getColumn($columnName) )
- #if ($column.isMultipleFK())
- #set ( $relCol = "$relCol$column.JavaName" )
+ #if ($column.isMultipleFK() || $tblFK.Name.equals($table.Name) )
+ #if ( $tblFK.Name.equals($table.Name) )
+ ## This means we have a self-referencing foreign key. We
+ ## will need to build more explicit method names so get
+ ## "related to" column in addition to the "related by" column.
+
+ #set ( $colFKName = $fk.LocalForeignMapping.get($columnName) )
+
+ ## This will get the "foreign" table column name but since
+ ## the foreign and local table names are the same we can
+ ## still get the column from this table
+
+ #set ( $relatedToCol = $tblFK.getColumn( $colFKName ).JavaName )
+
+ #end
+ #set ( $relCol = "$relCol$column.JavaName" )
#end
#end
@@ -1153,8 +1218,13 @@
#set ( $pCollName = "${className}s" )
#set ( $pCollNameNoS = "${className}" )
#else
- #set ( $pCollName = "${className}sRelatedBy$relCol" )
- #set ( $pCollNameNoS = "${className}RelatedBy$relCol" )
+ #if ( $tblFK.Name.equals($table.Name) )
+ #set ( $pCollName = "${className}sWhose${relCol}IsThis${relatedToCol}" )
+ #set ( $pCollNameNoS =
+"${className}Whose${relCol}IsThis${relatedToCol}" )
+ #else
+ #set ( $pCollName = "${className}sRelatedBy$relCol" )
+ #set ( $pCollNameNoS = "${className}RelatedBy$relCol" )
+ #end
#end
${list}v = get${pCollName}();
@@ -1165,7 +1235,6 @@
((Persistent) v.get(i)).setNew(true);
}
#set ( $list = "" )
- #end
#end
copyObj.setNew(true);
#end
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>