Re: toMany relationship returning null

From: Mike Kienenberger (mkienen..mail.com)
Date: Thu Sep 08 2005 - 14:35:19 EDT

  • Next message: Kevin Menard: "Re: Cayenne 1.2 + PostgreSQL"

    I'm not fond of introducing a dependency on ToManyList since this
    class could change in the future. I suppose you could get around that
    with some kind of ToManyListFactory.

    You're still going to have the issue that you can't call
    addTo/removeFrom while the object is unregistered since any objects
    added won't be properly registered when the object finally is
    registered, so I still think you're better off using "return (null ==
    value) ? Collections.EmptyList : value" in your getList method
    instead.

    -Mike

    On 9/8/05, Gili <cowwo..bs.darktech.org> wrote:
    >
    > You are quite right. Mike and I discussed this on IRC. The new patch
    > initializes ToManyList references instead. As far as I can tell, this
    > will fix the problems you mention. Are there other problems that we need
    > to be aware of? Any idea on how to improve this patch further?
    >
    > Gili
    >
    > Andrus Adamchik wrote:
    > > There *is* a difference in registered and unregistered DataObject
    > > behavior, period. This patch doesn't change that, it doesn't solve
    > > anything, and instead introduces an inconsistency by adding an
    > > immutable collection of the wrong type to the mix. Won't help you if
    > > you call "addToXyz" down the line and get an
    > > UnsupportedOperationException.
    > >
    > > -1 for the patch.
    > >
    > > Andrus
    > >
    > >
    > >
    > > On Sep 8, 2005, at 1:28 PM, Gili wrote:
    > >
    > >>
    > >> I've attached a patch which fixes this, in case anyone is
    > >> interested. Because we do this initialization in the template I think
    > >> there will be extremely little overhead (since we know ahead of time
    > >> which relationships are toMany). What do the rest of you think?
    > >>
    > >> Gili
    > >>
    > >> Mike Kienenberger wrote:
    > >>
    > >>> Moving this to dev.
    > >>> Actually, I wonder if the behavior is consistent even between NEW and
    > >>> other objects in other states.
    > >>> I'm thinking a toMany relationship will always return null if the
    > >>> relationship hasn't been initialized, regardless of whether the DO is
    > >>> registered or not.
    > >>> So null really means that the object's toMany relationship hasn't been
    > >>> initialized, and maybe that's a bad way to handle it since the methods
    > >>> are "add/remove/get" rather than "set/get"
    > >>> On 9/8/05, Mike Kienenberger <mkienen..mail.com> wrote:
    > >>>
    > >>>> I'd be "-0" on a patch for this.
    > >>>>
    > >>>> It'd add overhead because each DataObject would have to iterate over
    > >>>> the ObjEntity's attributes to determine if the value should be set to
    > >>>> EMPTY_LIST.
    > >>>>
    > >>>> Since I almost never work with DOs outside of a DataContext, I have no
    > >>>> issues with the DO returning null in those cases, and I consider it
    > >>>> useful to throw an error if I try to read a toMany relationship from
    > >>>> an unregistered object.
    > >>>>
    > >>>> On the other hand, if people who actually perform a lot of work on
    > >>>> unregistered objects think this makes sense, I have no compelling
    > >>>> reasons against it, either. I can understand the claim that the
    > >>>> behavior isn't consistent between registered and unregistered objects.
    > >>>>
    > >>>> On 9/8/05, Gili <cowwo..bs.darktech.org> wrote:
    > >>>>
    > >>>>
    > >>>>> That is possible. As far as I can tell, referencing
    > >>>>> Collections.EMPTY_LIST consumes as much resources as pointing to
    > >>>>> null.
    > >>>>> Is there any reason we don't initialize delegates' lists to
    > >>>>> Collections.EMPTY_LIST? I could contribute a patch if necessary, but
    > >>>>> does this sound reasonable to everyone?
    > >>>>>
    > >>>>> Gili
    > >>>>>
    > >>>>> Eric Schneider wrote:
    > >>>>>
    > >>>>>
    > >>>>>> Gili,
    > >>>>>>
    > >>>>>> Sounds like the object was never registered, or somehow it's
    > >>>>>> persistence state is transient. Normally, toMany relationships
    > >>>>>> will
    > >>>>>> always return an empty List if there are no related objects.
    > >>>>>>
    > >>>>>> Eric
    > >>>>>>
    > >>>>>> On Sep 7, 2005, at 4:02 PM, Gili wrote:
    > >>>>>>
    > >>>>>>
    > >>>>>>
    > >>>>>>> Hi,
    > >>>>>>>
    > >>>>>>> I'm expecting a toMany relationship to return an empty list if
    > >>>>>>> empty and it seems to return null. Is this by design (I can't
    > >>>>>>> seem to
    > >>>>>>> find it documented anywhere). Does this mean I have to check
    > >>>>>>> for both
    > >>>>>>> null or an empty list everywhere in my code or is there an
    > >>>>>>> easier way?
    > >>>>>>>
    > >>>>>>> Thanks,
    > >>>>>>> Gili
    > >>>>>>> --
    > >>>>>>> http://www.desktopbeautifier.com/
    > >>>>>>>
    > >>>>>>>
    > >>>>>>
    > >>>>>>
    > >>>>>>
    > >>>>> --
    > >>>>> http://www.desktopbeautifier.com/
    > >>>>>
    > >>>>>
    > >>>>
    > >>>>
    > >>
    > >> --
    > >> http://www.desktopbeautifier.com/
    > >> Index: cayenne/src/cayenne/resources/dotemplates/superclass.vm
    > >> ===================================================================
    > >> RCS file: /cvsroot/cayenne/cayenne/src/cayenne/resources/
    > >> dotemplates/superclass.vm,v
    > >> retrieving revision 1.10
    > >> diff -u -r1.10 superclass.vm
    > >> --- cayenne/src/cayenne/resources/dotemplates/superclass.vm 20 Nov
    > >> 2004 19:51:09 -0000 1.10
    > >> +++ cayenne/src/cayenne/resources/dotemplates/superclass.vm 8 Sep
    > >> 2005 17:15:45 -0000
    > >>.. -3,6 +3,7 @@
    > >>
    > >> #end
    > >> #if( ${classGen.isContainingListProperties()} )
    > >> +import java.util.Collections;
    > >> import java.util.List;
    > >>
    > >> #end
    > >>.. -13,6 +14,15 @@
    > >> */
    > >> public class ${classGen.superPrefix}${classGen.className} extends
    > >> $classGen.superClassName {
    > >>
    > >> +## Create constructor
    > >> + public ${classGen.superPrefix}${classGen.className}() {
    > >> +#foreach( $rel in ${classGen.Entity.DeclaredRelationships} )
    > >> +#if( $rel.ToMany )
    > >> + writeProperty("${rel.Name}", Collections.EMPTY_LIST);
    > >> +#end
    > >> +#end
    > >> + }
    > >> +
    > >> ## Create property names
    > >> #foreach( $attr in ${classGen.Entity.DeclaredAttributes} )
    > >> #set( $classGen.Prop = $attr.Name )## let controller know about
    > >> current property
    > >>
    > >
    > >
    >
    > --
    > http://www.desktopbeautifier.com/
    >
    >
    > #if( ${classGen.isUsingPackage()} )
    > package ${classGen.packageName};
    >
    > #end
    > #if( ${classGen.isContainingListProperties()} )
    > import java.util.List;
    > import org.objectstyle.cayenne.access.ToManyList;
    >
    > #end
    > /** Class ${classGen.superPrefix}${classGen.className} was generated by Cayenne.
    > * It is probably a good idea to avoid changing this class manually,
    > * since it may be overwritten next time code is regenerated.
    > * If you need to make any customizations, please use subclass.
    > */
    > public class ${classGen.superPrefix}${classGen.className} extends $classGen.superClassName {
    >
    > ## Create constructor
    > public ${classGen.superPrefix}${classGen.className}() {
    > #foreach( $rel in ${classGen.Entity.DeclaredRelationships} )
    > #if( $rel.ToMany )
    > #set( $classGen.Prop = $rel.Name )## let controller know about current property
    > writeProperty("${rel.Name}", new ToManyList(this, ${classGen.propAsConstantName}_PROPERTY));
    > #end
    > #end
    > }
    >
    > ## Create property names
    > #foreach( $attr in ${classGen.Entity.DeclaredAttributes} )
    > #set( $classGen.Prop = $attr.Name )## let controller know about current property
    > public static final String ${classGen.propAsConstantName}_PROPERTY = "${attr.Name}";
    > #end
    > #foreach( $rel in ${classGen.Entity.DeclaredRelationships} )
    > #set( $classGen.Prop = $rel.Name )## let controller know about current property
    > public static final String ${classGen.propAsConstantName}_PROPERTY = "${rel.Name}";
    > #end
    >
    > #if( $classGen.Entity.DbEntity )
    > #foreach( $idAttr in ${classGen.Entity.DbEntity.PrimaryKey} )
    > #set( $classGen.Prop = $idAttr.Name )## let controller know about current property
    > public static final String ${classGen.propAsConstantName}_PK_COLUMN = "${idAttr.Name}";
    > #end
    > #end
    >
    > ## Create attribute set/get methods
    > #foreach( $attr in ${classGen.Entity.DeclaredAttributes} )
    > #set( $classGen.Prop = $attr.Name )## let controller know about current property
    > #if ("true" != "${classGen.getEntity().isReadOnly()}")
    > public void set${classGen.cappedProp}($classGen.formatJavaType(${attr.Type}) $classGen.formatVariableName(${attr.Name})) {
    > writeProperty("${attr.Name}", $classGen.formatVariableName(${attr.Name}));
    > }
    > #end
    > public $classGen.formatJavaType(${attr.Type}) get${classGen.cappedProp}() {
    > return ($classGen.formatJavaType(${attr.Type}))readProperty("${attr.Name}");
    > }
    >
    >
    > #end
    > ##
    > ## Create list add/remove/get methods
    > #foreach( $rel in ${classGen.Entity.DeclaredRelationships} )
    > #set( $classGen.Prop = $rel.Name )## let controller know about current property
    > #if( $rel.ToMany )
    > #if ( ! $rel.ReadOnly )
    > public void addTo${classGen.cappedProp}($classGen.formatJavaType(${rel.TargetEntity.ClassName}) obj) {
    > addToManyTarget("${rel.name}", obj, true);
    > }
    > public void removeFrom${classGen.cappedProp}($classGen.formatJavaType(${rel.TargetEntity.ClassName}) obj) {
    > removeToManyTarget("${rel.name}", obj, true);
    > }
    > #end
    > public List get${classGen.cappedProp}() {
    > return (List)readProperty("${rel.name}");
    > }
    > #else
    > #if ( !${classGen.getEntity().isReadOnly()} && !$rel.ReadOnly )
    > public void set${classGen.cappedProp}($classGen.formatJavaType(${rel.TargetEntity.ClassName}) $classGen.formatVariableName(${rel.name})) {
    > setToOneTarget("${rel.name}", $classGen.formatVariableName(${rel.name}), true);
    > }
    > #end
    >
    > public $classGen.formatJavaType(${rel.TargetEntity.ClassName}) get${classGen.cappedProp}() {
    > return ($classGen.formatJavaType(${rel.TargetEntity.ClassName}))readProperty("${rel.name}");
    > }
    > #end
    >
    >
    > #end
    > }
    >
    >
    >



    This archive was generated by hypermail 2.0.0 : Thu Sep 08 2005 - 14:35:20 EDT