Hi Michael,

I have ended up doing exactly what you did except in Cayenne 4.x using a bit of a recursive call cheat as I know the ClientBaseAdmin.getObjectContext() should always return the same ObjectContext in both the Parent and Child objects.  I left the commented out code which was causing me the grief on the commitChanges when I used the original ObjectContext.

Parent code

ObjectContext oc = ClientBaseAdmin.getObjectContext();
CommunicationLog cl = oc.newObject(CommunicationLog.class);
cl.setCommunicationDT(LocalDateTime.now());
cl.setCommunicationType( CommunicationType.find("Email"));
cl.setDescription(cl.getCommunicationType().getDescription() + ",notification msg," + win.selectedItem.getCode() + ",to," + contact.getEmail());
oc.commitChanges();

And the child code

    public static CommunicationType find(String value) {
        CommunicationType result = null;
        result = ObjectSelect.query(CommunicationType.class)
                .where(CommunicationType.DESCRIPTION.eq(value))
                .selectFirst(ClientBaseAdmin.getObjectContext());

        if (result == null) {
            ObjectContext oc = ORMCayenne.getServerRuntime(UI_ClientBase.RESOURCE_DIR + UI_ClientBase.MY_CAYENNE_XML_FILE).newContext();
            // ObjectContext oc = ClientBaseAdmin.getObjectContext();
            CommunicationType ct = oc.newObject(CommunicationType.class);
            ct.setDescription(value);
            oc.commitChanges();
            result = find(value);
            //result = ct;
        }
        return result;
    }



On 20/09/17 09:54 AM, Michael Gentry wrote:
Hi Andrew,

I'm jumping in late here, but I did something very similar to what you are
trying to do with CommunicationType.find().  I create (auto-populate) a
reference table record based upon some enum values if missing, or just
return the reference table record if I find it.  I do this in a completely
separate (not a child) DataContext and copy the result into the main
destination context passed into the method.

This is from 3.1, so might vary a bit on the last line if you are on 4.x
(I'm using a deprecated method there and haven't updated yet):

     public static ContractDesignation fetch(ObjectContext
destinationContext,
                                             ContractMethodType
contractMethod,
                                             ContractType       contractType,
                                             FundingVehicleType
fundingVehicle)
     {
         ContractDesignation contractDesignation = null;
         DataContext         dataContext         =
CayenneUtils.createDataContext();
         Expression          exp1                =
ExpressionFactory.matchExp(CONTRACT_METHOD_PROPERTY, contractMethod);
         Expression          exp2                =
ExpressionFactory.matchExp(CONTRACT_TYPE_PROPERTY, contractType);
         Expression          exp3                =
ExpressionFactory.matchExp(FUNDING_VEHICLE_PROPERTY, fundingVehicle);
         Expression          exp                 =
exp1.andExp(exp2).andExp(exp3);
         SelectQuery         query               = new
SelectQuery(ContractDesignation.class, exp);

         @SuppressWarnings("unchecked")
         List<ContractDesignation> contractDesignations =
dataContext.performQuery(query);

         // If no contract designation is found, create a new one, else use
the
         // one that was found (throwing an exception if too many are found).
         if (contractDesignations.isEmpty())
         {
             contractDesignation =
dataContext.newObject(ContractDesignation.class);

             contractDesignation.setContractMethod(contractMethod);
             contractDesignation.setContractType(contractType);
             contractDesignation.setFundingVehicle(fundingVehicle);

             dataContext.commitChanges();
         }
         else if (contractDesignations.size() == 1)
         {
             contractDesignation = contractDesignations.get(0);
         }
         else
         {
             // There can be only one!
             throw new TooManyResultsException("There can be at most one
match for ContractDesignation contractMethod='" + contractMethod +
                                               "' / contractType='" +
contractType +
                                               "' / fundingVehicle='" +
fundingVehicle + "'");
         }

         // Pull the fetched ContractDesignation into the destination
ObjectContext.
         return (ContractDesignation)
DataObjectUtils.objectForPK(destinationContext,
contractDesignation.getObjectId());
     }




On Wed, Sep 20, 2017 at 9:16 AM, Andrew Willerding <[email protected]
wrote:
I thought of this too but I'm trying to create the child object
dynamically without having any impact on the parent object or making the
coder of the parent object worry about creating child objects first.



On 20/09/17 08:45 AM, Nikita Timofeev wrote:

Other option is to create CommunicationType first and then just use it:

CommunicationType type = CommunicationType.find("Email");

CommunicationLog cl = oc.newObject(CommunicationLog.class);
cl.setCommunicationDT(LocalDateTime.now());
cl.setCommunicationType(type);
oc.commitChanges();

In this case everything will be in one context, and you can freely
commit CommunicationType to DB.

On Wed, Sep 20, 2017 at 3:37 PM, Mike Kienenberger <[email protected]>
wrote:

I haven't followed the details, so I don't know if using a separate
object context is necessary, but all you need to do to move a data
object (in your case "child") to a different context is to call
parentContext.localObject(childDataObject) which will return
childDataObjectInParentContext.   Note that this requires either a
hollow or committed childDataObject as a modified (uncommitted) or new
(uncommitted) data object's changes only exist in the
childObjectContext.

I may also have the syntax for localObject() wrong as I'm probably
using an older version of Cayenne than you are.



On Wed, Sep 20, 2017 at 8:30 AM, Andrew Willerding
<[email protected]> wrote:

I guess my issue is with how independent the commits can be.  Now that I
better understand this, my code example works as Nikita suggested (with
the
fix for my problem with the PrimaryKey table) and if I drop the commit
during the creation of the child object otherwise the commit during the
child creation will fail because the parent has not yet been assigned
with
the child object.

My objective is that I want the creation of the child to be completely
independent of the parent object for this situation. To do this I have
to
create a new ObjectContext  for the child object to make it independent
of
the parent's ObjectContext but when I return the newly created child
object
to the parent object I get the following

Caused by: org.apache.cayenne.CayenneRuntimeException: [v.4.0.B1 Jun
02 2017
15:11:18] Cannot set object as destination of relationship
communicationType
because it is in a different ObjectContext
      at
org.apache.cayenne.CayenneDataObject.willConnect(CayenneData
Object.java:399)
      at
org.apache.cayenne.CayenneDataObject.setToOneTarget(CayenneD
ataObject.java:355)
      at
com.callistacti.clientbase.Database.auto._CommunicationLog.s
etCommunicationType(_CommunicationLog.java:65)
      at
com.callistacti.clientbase.Panel.PanelGroups.windowClose(Pan
elGroups.java:423)

So what I then needed to do is lookup the child object with the same
ObjectContext that the parent was using.

Out of curiousity, is there any simpler way of doing this?



On 19/09/17 09:16 AM, Aristedes Maniatis wrote:

On 19/9/17 11:01PM, Andrew Willerding wrote:

The CommunicationType object does not yet exist permanently in the
database table so how or when does it generate the primary key for the
object to be referenced by another object before it is committed?  I
was
hoping to not have to worry about this as a "high-level" user of
Cayenne.

You are right, you don't need to worry about all that. Just create one
context and save it at the end. Cayenne tracks the relationships
between
objects with a temporary key internally, but you don't need to think
about
that. Once Caynenne commits, everything is assigned proper primary
keys and
joined.

Ari





Reply via email to