Re: toMany relationship returning null

From: Gili (cowwo..bs.darktech.org)
Date: Thu Sep 08 2005 - 14:25:36 EDT

  • Next message: Andrus Adamchik: "Re: toMany relationship returning null"

            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:25:35 EDT