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.primaryKeyDictionaryForObjectAndPrimaryK
eyAttributes: ";
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.primaryKeyDictionaryForObjectAndPrimaryKe
yAttributes: attributeName = cpkFkCompanyID ... no value from
EO ... setting gotAllKeys to FALSE
[2008-06-10 11:18:33 CDT] <main>
DatabaseContextDelegate1.primaryKeyDictionaryForObjectAndPrimaryKe
yAttributes: 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.primaryKeyDictionaryForObjectAndPrimaryKe
yAttributes:
<-- (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.prepareForSaveWithCoordi
nator(EODatabaseContext.java:5885)
at
com.webobjects.eocontrol.EOObjectStoreCoordinator.saveChangesInEdi
tingContext(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 =