Programming__WebObjects-EOF-Using EOF-Primary Keys

Version 2.1 by smmccraw on 2007/07/08 09:44

Accessing Primary Keys

When your primary key is not visible as a class property, the documented way to obtain the primary key values is with EOUtilities.primaryKeyForObject(). This works, but in the case where you have a single non-compound primary key, you need a few more lines of code to pull out this pk value. More troubling, the EOUtilities method, when called with a fault, will cause the fault to be resolved. The fault does not really need to be resolved to get the primary key, as the pk value is present even in the fault. 

Here's a method that works for EOs which have a single Integer primary key, to pull out that pk value, and without resolving a fault (possibly causing a trip to the db) when called on a fault value:

    public static Integer singleIntegerPrimaryKeyForObject(EOEnterpriseObject eo) {
        EOEditingContext ec = eo.editingContext();
        if (ec == null) {
           you don't have an EC!_Bad_EO._We_can_do_nothing._
return_null;
}
EOGlobalID_gid_=_ec.globalIDForObject(eo);
if_(gid.isTemporary())_{
no_pk_yet_assigned
return_null;
}
EOKeyGlobalID_kGid_=_(EOKeyGlobalID)_gid;
NSArray_keyValues_=_kGid.keyValuesArray();
if_(keyValues.count()_!= 1 ||
            !_(keyValues.objectAtIndex(0)_instanceof_Integer))
return_null;

return_(Integer)_keyValues.objectAtIndex(0);

}

YoucanalsousetheEOUtilitiesmethodbutpleasenotethat,atleastasofWO5,therearetwosomewhatundesirableaspectsofthisEOUtilitiescode:

  • IftheEOisafault,callingtheEOUtilitiesmethodwillcausethefaulttoberesolved,possiblyrequiringatriptothedb,eventhoughpkinfoisalreadypresentinthefaultandthetriptothedbisreallyunneccesaryatthispoint.
  • IftheEOhasjustbeeninsertedandnotyetcommitted,itdoesnothaveapkyet,andtheEOUtilitiescodewillthrowanexception.Andit'snotalogicalexceptioncorrespondingto"nopkyet",butrathersomeuncaughtexceptionthrownbysomesurprisedApplecode.

AssigningPrimaryKeys

UsuallyEOFdoesn'tgetaroundtogenerating/insertingprimarykeysuntilyouactuallysaveChanges()onanEOEditingContextcontainingnewlycreatedabout-to-besavedEOEnterpriseObjects.

ButsometimesyouwanttoassignapermanentpkassoonasyoucreatetheEOEnterpriseObject,notwaituntilit'sactuallycommittedtothedb.Here'ssomecodetodothat,which,inagivensituation,willgeneratethepkinthesamewaythatEOFwouldhavegenerateditlater,butdoitrightaway.Thanks_to_Pierre_Barnard

public_voidinsertObjectWithGlobalID(EOEnterpriseObject_eo,_EOGlobalID_globalID)
{
EOEntity_entity_=_EOUtilities.entityNamed(this,_eo.entityName());
EODatabaseContext_dbContext_=_EOUtilities.databaseContextForModelNamed(this,_entity.model().name());
NSDictionary_pkDict_=_primaryKeyDictionaryForDatabaseContextAndEntity(dbContext,_entity);

if_(pkDict_==_null_||_pkDict.count()_!= 1)
    {
      NSLog.err.appendln("Failed to generate primary key for entity " + entity.name() + ", or pk is compound.");
    } else
    {
      Object pk = pkDict.allValues().lastObject();
      globalID = EOKeyGlobalID.globalIDWithEntityName(entity.name(), new Object[] { pk });
    }
     
    super._insertObjectWithGlobalID(eo, globalID);
  }
 
  public static NSDictionary primaryKeyDictionaryForDatabaseContextAndEntity(EODatabaseContext dbContext, EOEntity entity) {
    NSDictionary pk = null;
    try {
      dbContext.lock();
      EOAdaptorChannel adaptorChannel = dbContext.availableChannel().adaptorChannel();
      if (!adaptorChannel.isOpen()) {
        adaptorChannel.openChannel();
      }
      pk = (NSDictionary) adaptorChannel.primaryKeysForNewRowsWithEntity(1, entity).lastObject();
    }
    catch (Exception e) {
      NSLog.err.appendln("Can't get primary keys for entity " + entity.name() + "  " + e);
    }
    finally {
      dbContext.unlock();
      return pk;
    }
  }

There are 2 insertObjectWithGlobalID methods in EOEditingContext. One has a name prefixed by an underscore. The regular one seems to call that one. For most uses overriding the regular one should be sufficient. With JavaClient however, the regular one is not called. On the server side only the one with the underscore is called. You'll have to override the method with the underscore in the server side context as the above will work only on the server.

If you use nested ECs in a standard WO application you might have to override the same semi-private method should you want the parent EC to handle PK creation.

BTW, I believe EOF does some optimization by retrieving PKs in batches rather than one by one. You lose that when using the above code.

Category:WebObjects