|
Server-Side notes
When the sync function performs
a DELETE operation, it gets the primary key ID from the OriginalObject
of the Sync method, as the ClientObject is NULL. For update and
insert operations, use the ClientObject key value.
When you do an INSERT operation, the CFC checks whether the
OriginalObject parameter of the sync method is a simple value, as
in the following code: {NOT IsSimpleValue(OriginalObject)}
In
an INSERT operation, OriginalObject passed to the Sync function
is null. So if you attempt to retrieve any of its properties, you
get a Method NOT Found error. For Example, OriginalObject.GetID
results in a Method GetID() not found error. So, for Insert operation,
use ClientObject to access various fields.
While a ColdFusion application can use cfquery to directly
manage the database, most AIR applications are expected to use the
ORM feature. The discussion here uses ColdFusion ORM for server-side
data management.
You may see the following kind of error message if you are
using ColdFusion 8 Remoting with AIR offline applications, which
have server side " Sync" method using ORM EntitySave()/ EntityDelete() methods. Error handling message: flex.messaging.MessageException: Unable to invoke CFC - a different object with the same identifier value was already associated with the session: [address#1].
Root cause:org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [address#1]
You
may also encounter this error with ColdFusion 9 Remoting but only
for EntityDelete method.
To resolve this
sort of error, call your EntitySave/EntityDelete method in
following way in "Sync" method.
<cfif operation eq "INSERT" OR operation eq "UPDATE">
<cfset obj = ORMGetSession().merge(clientobject)>
<cfset EntitySave(obj)>
<cfelseif operation eq "DELETE">
<cfset obj = ORMGetSession().merge(originalobject)>
<cfset EntityDelete(obj)>
</cfif>
In case of a conflict, the sync function
returns an array of "CFIDE.AIR.Conflict" objects
to the client. There are four properties a conflict object can have: operation,serverobject,clientobject,originalobject.
The serverobject property
of the conflict object must be a user-defined CFC type that represents
the server-side database table. The following example generates
a conflict object with a valid ServerObject property of type employee.cfc,
which represents the Employee table:
<cfset serverobject = EntityLoadByPK("employee",originalobject.getId())>
<cfset conflict = CreateObject("component","CFIDE.AIR.conflict")>
<cfset conflict.serverobject = serverobject>
<cfset conflict.clientobject = clientobject>
<cfset conflict.originalobject = originalobject>
<cfset conflict.operation = operation>
<cfset conflicts[conflictcount++] = conflict>
<cfreturn conflicts>
If you are using ColdFusion
ORM, you can replace the preceding example with the following code. <cfset conflict = CreateObject("component","CFIDE.AIR.Conflict")
<cfset serverobject = EntityLoadByPK("employee",#res.IDENTITYCOL#)>
<cfset conflict.SetServerobject(serverobject)>
When an AIR client with stale data tries to update an already
deleted record from the database, server throws the conflict, and
the client's conflict handle, which has the KeepAllServerObjects or KeepServerObject method
accepts the changes from the server. However, the client method does
not delete the stale record, which no longer exists in the server
database, from the client database.
To prevent this issue:
The serverObject property of the conflict object returned by the
server must be null, if the record that the client requests for
updating is no longer in the database. For example:
<cfset serverobject = EntityLoadByPK("employee",originalobject.getId())>
<!----If the operation is INSERT, serverObject is also NULL.hence NEQ condition---->
<cfif not isdefined('serverobject') and operation NEQ "INSERT" >
<cflog text="CONFLICT::SERVER OBJECT NOT FOUND, RECORD MAY BE DELETED ALREADY">
<cfset conflict = CreateObject("component","CFIDE.AIR.conflict")>
<cfset conflict.clientobject = clientobject>
<cfset conflict.originalobject = originalobject>
<cfset conflict.operation = operation>
<cfset conflicts[conflictcount++] = conflict>
<cfcontinue>
</cfif>
|