Ok I really really hope someone can help me with this problem:
I have an existing database which my app has been coded with
a DatabaseContextDelegate to handle primary key generation.
I model all Entities such that primary keys and foreign keys
are NOT generated (getters or setters) in the class files.
My DatabaseContextDelegate is as follows:
public class DatabaseContextDelegate1 extends Object {
protected static Object utilValueForKey( Object object,
String key )
{
try {
return NSKeyValueCoding.Utility.valueForKey
( object, key );
}
catch ( NSKeyValueCoding.UnknownKeyException e )
{
NSLog.err.appendln
( "DatabaseContextDelegate1.utilValueForKey: e = " + e );
e.printStackTrace();
NSLog.err.appendln( "returning null ..." );
return null;
}
}
// --- EODatabaseContext.Delegate methods --
public NSDictionary databaseContextNewPrimaryKey
( EODatabaseContext databaseContext,
Object object,
EOEntity entity )
{
String debugPrefix =
"DatabaseContextDelegate1.databaseContextNewPrimaryKey: ";
// get primary key attributes (description of them)
from entity (description of) requesting new primary key(s)
NSArray primaryKeyAttributes =
entity.primaryKeyAttributes();
// if *NOT* EXACTLY ONE primary key attribute,
// return primary key dictionary for object and
primary key attributes
// NOTE: may return null if object does not have
values for ALL primary key attributes, thus
// allowing someone else (superclass) to handle
primary keys
if ( primaryKeyAttributes.count() != 1 )
{
NSDictionary primaryKeyDictionary =
primaryKeyDictionaryForObjectAndPrimaryKeyAttributes
( object, primaryKeyAttributes );
return primaryKeyDictionary;
}
// NOTE: at this point primary key attributes
contain EXACTLY ONE primary key attribute
// get description of first (and only) primary key
attribute
EOAttribute primaryKeyAttribute = (EOAttribute)
primaryKeyAttributes.objectAtIndex(0);
if ( primaryKeyAttribute.adaptorValueType() !=
EOAttribute.AdaptorBytesType )
{
// We support only number keys, so call the
superclass
return null;
}
// setup variables for further processing
EOAdaptorChannel channel = null;
NSDictionary primaryKeyDictionary = null;
try
{
databaseContext.lock();
channel = databaseContext.availableChannel
().adaptorChannel();
if ( ! channel.isOpen() )
channel.openChannel();
NSDictionary row = null;
try
{
EOSQLExpressionFactory factory =
new EOSQLExpressionFactory
( databaseContext.adaptorContext().adaptor() );
EOSQLExpression getRowExpr =
factory.expressionForString( "SELECT top 1 VALUE FROM
vNewUUID" );
channel.evaluateExpression( getRowExpr );
row = channel.fetchRow();
channel.cancelFetch();
}
catch ( Throwable localException ) {
channel.cancelFetch();
}
if ( row != null )
{
NSData newUUID = (NSData)row.objectForKey
( "VALUE" );
if ( newUUID != null )
primaryKeyDictionary = new NSDictionary
( (Object)newUUID, (Object)primaryKeyAttribute.name() );
else
NSLog.err.appendln( debugPrefix + "got
NULL newUUID for object " + object );
}
else NSLog.err.appendln( debugPrefix + "could
not set keys for object " + object );
}
catch ( Throwable ex ) {
System.err.println( ex.toString() );
}
finally
{
if ( channel.isOpen() &&
channel.isFetchInProgress() )
channel.cancelFetch();
databaseContext.unlock();
}
// NOTE: may return null allowing someone else
(superclass) to handle primary keys
return primaryKeyDictionary;
}
// --- utility ---
public NSDictionary
primaryKeyDictionaryForObjectAndPrimaryKeyAttributes
( Object object,
NSArray primaryKeyAttributes )
{
String debugPrefix =
"DatabaseContextDelegate1.primaryKeyDictionaryForObjectAndPrima
ryKeyAttributes: ";
int primaryKeyAttributeCount =
primaryKeyAttributes.count();
// initialize "got all keys" to TRUE (to be set to
FALSE if one is found missing)
boolean gotAllKeys = true;
// initialize empty primary key dictionary
NSMutableDictionary primaryKeyDictionary = new
NSMutableDictionary();
// loop thru primary key attributes
for ( int x = 0 ; x < primaryKeyAttributeCount ; x++ )
{
// get next (primary key) attribute
EOAttribute attribute = (EOAttribute)
primaryKeyAttributes.objectAtIndex(x);
// get attribute name
String attributeName = attribute.name();
// get value from object for attribute by
attribute name
Object valueFromEO = utilValueForKey( object,
attributeName );
// if the object has a value, add it to the
primary key dictionary
// otherwise, flag "got all keys" as FALSE
if ( valueFromEO != null )
primaryKeyDictionary.setObjectForKey
( valueFromEO, attributeName );
else
{
System.out.println( debugPrefix +
"attributeName = " + attributeName + " ... no value from
EO ... setting gotAllKeys to FALSE" );
gotAllKeys = false;
}
}
// if we got values for ALL keys from the object,
// return the primary key dictionary
if ( gotAllKeys )
{
NSLog.debug.appendln( debugPrefix + "returning
primary key " + primaryKeyDictionary + " from object " +
object );
return primaryKeyDictionary;
}
// NOTE: at this point we did NOT get values for ALL
keys from the object ...
// We support only simple primary keys, data keys
are handled by superclass
// thus DO NOT return the primary key dictionary,
instead return NULL (nothing)
NSLog.err.appendln( debugPrefix + "could not set
keys for object " + object );
return null;
}
}
It's all straight forward and works for single primary
keys. Where it has problems is when a compound primary key
element is a foreign key (a relationship). I think I need
the primaryKeyDictionaryForObjectAndPrimaryKeyAttributes
function to correctly build the primary key dictionary, but
I don't know how to do it, especially with the primary key
components being foreign keys (used in relationships that
are set, of course). I simplified the test case:
Company:
pkID (binary)
*name (String)
Product:
cpkFkCompanyID (binary)
*cpkCode (String)
*name (String)
*only these items have diamonds on them in EOModeler
Company <--->> Product
default Product. cpkCode value is "xxx"
EODatabaseContext.setDefaultDelegate( new
DatabaseContextDelegate1() );
EOEditingContext ec = new EOEditingContext();
EOEnterpriseObject company =
EOUtilities.createAndInsertInstance( ec, "Company" );
EOEnterpriseObject product =
EOUtilities.createAndInsertInstance( ec, "Product" );
product.addObjectToBothSidesOfRelationshipWithKey
( company, "company" );
product.takeValueForKey( "xxx", "cpkCode" );
ec.saveChanges();
Sounds simple enough right?
Here's my error:
DatabaseContextDelegate1.primaryKeyDictionaryForObjectAndPrimar
yKeyAttributes: attributeName = cpkFkCompanyID ... no value
from EO ... setting gotAllKeys to FALSE
[2008-06-10 11:18:33 CDT] <main>
DatabaseContextDelegate1.primaryKeyDictionaryForObjectAndPrimar
yKeyAttributes: could not set keys for object {values =
{Company = "<com.webobjects.eocontrol.EOGenericRecord e2350a
<EOTemporaryGlobalID: 0 0 -64 -88 2 1 0 0 -40 42 1 0 0 0 1
26 115 69 -122 118 -65 -101 100 1>>"; cpkCode = "xxx"; };
this = "<com.webobjects.eocontrol.EOGenericRecord abcd5e
<EOTemporaryGlobalID: 0 0 -64 -88 2 1 0 0 -40 42 2 0 0 0 1
26 115 69 -122 118 -65 -101 100 1>>"; }
<-- (2)
DatabaseContextDelegate1.primaryKeyDictionaryForObjectAndPrimar
yKeyAttributes:
<-- (1) DatabaseContextDelegate1.databaseContextNewPrimaryKey:
[2008-06-10 11:18:33 CDT] <main> A fatal exception occurred:
Adaptor [EMAIL PROTECTED] failed
to provide new primary keys for entity 'Product'
[2008-06-10 11:18:33 CDT] <main>
java.lang.IllegalStateException: Adaptor
[EMAIL PROTECTED] failed to
provide new primary keys for entity 'Product'
at
com.webobjects.eoaccess.EODatabaseContext.prepareForSaveWithCoo
rdinator(EODatabaseContext.java:5885)
at
com.webobjects.eocontrol.EOObjectStoreCoordinator.saveChangesIn
EditingContext(EOObjectStoreCoordinator.java:409)
at com.webobjects.eocontrol.EOEditingContext.saveChanges
(EOEditingContext.java:3226)
at Application.<init>(Application.java:31)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0
(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance
(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance
(Constructor.java:494)
at java.lang.Class.newInstance0(Class.java:350)
at java.lang.Class.newInstance(Class.java:303)
at com.webobjects.appserver.WOApplication.main
(WOApplication.java:323)
at Application.main(Application.java:16)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke
(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke
(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at com.webobjects._bootstrap.WOBootstrap.main
(WOBootstrap.java:71)
If anyone has ANY suggestions, please email me as soon as
possible!
Thanks!
= Robert =