Ok it looks likes like I have stumbled upon a strange timing issue... I made a quick SQL wrapper class for executing sql statements. However after .execute() is called, the SQLEvent.RESULT event is never fired, but the new entry in the DB is created as it should be. The really really odd part is if I put a setTimeout() just after calling execute() the event fires as expected.. I hope I'm missing something really obvious here... Here is a link to an example air app: http://www.massivepoint.com/airsqltest/AIRSQL.zip
And here is the code to the wrapper class:
if you look down at line 51 in the SQLRequest class, you will see the commented out setTimeout() method. To make everything work, just uncomment that line.. but to me this doesn't make any sense...
anyone have any thoughts? I'm totally stumped here...
package com.jac.sqlite
{//Package
import flash.data.SQLConnection;
import flash.data.SQLStatement;
import flash.events.EventDispatcher;
import flash.events.SQLErrorEvent;
import flash.events.SQLEvent;
import flash.utils.setTimeout;
public class SQLRequest extends EventDispatcher
{//SQLRequest Class
private var _callback:Function;
private var _dbConn:SQLConnection;
private var _query:String;
private var _params:Object;
private var _statement:SQLStatement;
public function SQLRequest(callback:Function, connection:SQLConnection, query:String, parameters:Object=null):void
{//SQLRequest
trace("Creating new SQL Request");
_callback = callback;
_dbConn = connection;
_query = query;
_params = parameters;
_statement = new SQLStatement();
_statement.sqlConnection = _dbConn;
_statement.text = _query;
if (_params != null)
{//assign
for (var param:String in _params)
{//params
trace("Setting Param: " + param + " to: " + _params[param]);
_statement.parameters[param] = _params[param];
}//params
}//assign
//setup events
_statement.addEventListener(SQLEvent.RESULT, handleResult, false, 0, true);
_statement.addEventListener(SQLErrorEvent.ERROR, handleError, false, 0, true);
}//SQLRequest
public function startLoad():void
{//execute
_statement.execute();
//setTimeout(handleTimeOut, 10000);
}//execute
//TEMP
private function handleTimeOut():void
{//handleTimeOut
trace("Executing: " + _statement.executing + " / " + executing);
}//handleTimeOut
private function handleResult(e:SQLEvent):void
{//handleResult
trace("Good SQL Request");
_callback(e);
dispatchEvent(e);
}//handleResult
private function handleError(e:SQLErrorEvent):void
{//handleError
trace("SQL Error: " + e.errorID + ": " + e.error);
//dispatchEvent(e);
}//handleError
public function get executing():Boolean
{//get executing
return _statement.executing;
}//get executing
public function get query():String { return _query; }
public function get statement():SQLStatement { return _statement; }
}//SQLRequest Class
}//Package
I think what you're missing here is garbage collection.
Haven't tested your code, but this could certainly be the source of the problem.
The reference
sqlReq
is local to the function and becomes unreacheable when the function returns. That makes it collectable. I guess there must be some code in the AIR runtime that collects garbage more agressively when there are sql connections involved. Because generally, you'll get away with not storing a ref to your object (at least in a web based environment, in my experience; this is a bug in such code, nevertheless; you just have to be in a bad day to experience it).The
setTimeout
masks this problem (or almost solves it, although in an unintended way), because thesetTimeout
function uses aTimer
internally. Running timers are not collected. So, the timer is alive and kicking and has a reference to yourSQLRequest
instance, which makes it reacheable, and so, not elligible for collection. If your DB call takes longer than the timeout though, you're back in the same situation.To solve this, store a ref to the object and dispose it properly when you're done.
Edit
Another option, if you don't want to change the way you calling code works, is storing a ref to the instance in a class-scoped (i.e. static) dictionary for the duration of the call (this dictionary shoul not use weak referenced keys for obvious reasons).
You are adding a hidden side effect to your method, which is not a sign of good design in general, but as long as you remove it when the call to the DB is finished (whether it succeded or not), you're safe, so I think the problem is more of style than anything else.
What I mean is something like this: