Example of PhoneGap's Database Support
Folks who have used Adobe AIR before already know (or hopefully know) about it's cool, built-in database support. Any AIR application (whether written in Flex or HTML - you do remember you can do AIR with HTML, right?) can make use of an embedded SQLite database. This is incredibly useful for applications and pretty darn easy to use as well. PhoneGap also has built in support for SQLite via their storage support. In this blog entry I'll walk you through a sample application I've created that makes use of the feature. I'll also point out some of the mistakes I made while writing so that hopefully you can avoid the issues I ran into.
First - it is important to carefully read PhoneGap's storage documentation. I don't know why - but the biggest issue I've had with PhoneGap so far has been around my incorrect assumptions not matching up with reality. I guess I shouldn't be surprised there, but with PhoneGap I've found it really critical to carefully read the docs. It isn't that PhoneGap is brittle per se, but you really need to ensure you know what you're doing. Consider yourself warned.
Along with PhoneGap's docs, you want to read the W3C spec for Web SQL Database support. It definitely helped fill in some things that the PhoneGap docs didn't cover. As an example, the PhoneGap docs enumerate the error codes for the SQLError object, but don't tell you the actual values that go with them. The W3C docs for SQLError though have both the labels and the codes.
For folks who are familiar with SQLite in AIR, there are two main differences I've found between the AIR and PhoneGap platforms.
First - it seems (although I've not confirmed this) that PhoneGap only supports asynchronous connections. That's not a bad thing typically, but I tend to prefer synchronous connections since they are a lot simpler to work with. They can lock your application up if you need to perform a particularly long operation, but I like having the option at least. I'll typically use two connections in an AIR app. I'll have a synchronous one for setup and then an asynchronous one to handle post-launch operations. Again - with PhoneGap you only have the asynchronous option.
Second - while both AIR and PhoneGap support transactions through SQLite, PhoneGap requires all SQL transactions to go through a transaction. This adds a level of complexity to your code that may be a bit confusing at first, but once you've worked through a couple of examples it really isn't that big of a deal. It tended to bite me in the rear because I made the mistake of running "executeSQL" on my transaction object instead of "executeSql". The application reported it as a database error because the transaction callback was executing. This was confusing and hard to debug.
Ok - so let's talk about the application. My demo application is called SimpleNotes. It's a very poor, very limited note taking application. It's got a grand total of two screens. The first is a list of your current notes.

You can click a note to edit it, or simply click Add Note.

So how does it work? I've got an event handler that listens in for the deviceready event. This event is fired by PhoneGap when the hardware is ready for business time. I began by having it call a function named phoneReady:
2 //First, open our db
3 dbShell = window.openDatabase("SimpleNotes", 2, "SimpleNotes", 1000000);
4 //run transaction to create initial tables
5 dbShell.transaction(setupTable,dbErrorHandler,getEntries);
6}
In case your curious why I named the database twice - the first string is the database name while the second one is the display name. Much like my name is Raymond but I write it as G-Megabyte. (Now you know...) The second argument is the version. This can be used to shift the application from one database version to another. Normally you would update a database with SQL though so I'm not sure that folks would change this often in production. The last argument is the size. I picked 1000000 because I thought it looked nice on paper. Notice then we have a transaction to run setupTable. This is where I'll do my initial definitions.
2function setupTable(tx){
3 tx.executeSql("CREATE TABLE IF NOT EXISTS notes(id INTEGER PRIMARY KEY,title,body,updated)");
4}
As you can see, I use "IF NOT EXISTS" in my creation script. That makes it safe to run more than once. For information on valid SQL syntax for SQLite, check their docs. Ok, now if you remember the original transaction call, it had 2 more arguments. The second was a generic error handler, and the third was getEntries. First let's look at the error handler:
2 alert("DB Error: "+err.message + "\nCode="+err.code);
3}
Most likely, and especially for an application like this, any error should be logged, possibly emailed, and also result in the termination of the app itself. My application is pretty useless if it can't do what it needs to with the database. But how you handle your errors will be up to the individual application. Let's more on then to getEntries.
2function getEntries() {
3 dbShell.transaction(function(tx) {
4 tx.executeSql("select id, title, body, updated from notes order by updated desc",[],renderEntries,dbErrorHandler);
5 }, dbErrorHandler);
6}
So compared to the earlier transaction call I got a bit fancier here. I've embedded the function within the transaction call itself. It may be a bit hard to read there so let me rewrite this in pseudo-code so it is clear:
2 execute my sql, and on a good result, run renderEntries, and on a bad result, run dbErrorHandler
3If the transaction fails: run dbErrorHandler
I sometimes tried to put the result handler in the transaction call, but if you do that, you don't get passed the result of the SQL execution. So... don't do that. The second argument to executeSql (the empty array) is how bound parameters are handled. Since I have none I just pass it as an empty array. Now let's look at renderEntries.
2 doLog("render entries");
3 if (results.rows.length == 0) {
4 $("#mainContent").html("<p>You currently do not have any notes.</p>");
5 } else {
6 var s = "";
7 for(var i=0; i<results.rows.length; i++) {
8 s += "<li><a href='edit.html?id="+results.rows.item(i).id + "'>" + results.rows.item(i).title + "</a></li>";
9 }
10 $("#noteTitleList").html(s);
11 $("#noteTitleList").listview("refresh");
12 }
13}
So a good portion of this is generic DOM manipulation and jQuery Mobile related, but focus on how I use the results attribute. I can easily check the length and handle an empty database. I can also easily loop over them and access the data. Pretty simple, right? Let's look at one more function. When saving a note, I'm either saving an existing note or editing a new one. I've got code in there to handle that, but let's focus on the actual save operation.
2 //Sometimes you may want to jot down something quickly....
3 if(note.title == "") note.title = "[No Title]";
4 dbShell.transaction(function(tx) {
5 if(note.id == "") tx.executeSql("insert into notes(title,body,updated) values(?,?,?)",[note.title,note.body, new Date()]);
6 else tx.executeSql("update notes set title=?, body=?, updated=? where id=?",[note.title,note.body, new Date(), note.id]);
7 }, dbErrorHandler,cb);
8}
So before we start - I'll say that the note argument is a simple JavaScript object. It's got values for title, body, and possibly the ID. The cb argument is simply a call back to execute. Notice how I handle insert versus update logic. Also note how i handle the bound parameters. Very much like AIR. It looks like you can't do named parameters like you can in Adobe AIR, so make your get your positions right.
And that's pretty much it. There's a lot more logic around to handle my form editing and the like, but you can see that yourself in the attached zip. The zip contains an unsigned APK as well as a copy of the code. Note - when saving a note I've got a weird page display issue going on. Anyone who fixes that gets mega bonus points from the pool.
Edit: For folks downloading the code, please see Anne's comment here.

Thanks,
--Dave
SQLite uses a 'real' file. Therefore, someone should be able to find the file and get to it. In AIR you can encrypt the DB. I do not see that option here.
For LocalStorage, it is tied to the browser instance and the domain. LocalStorage should still work fine w/n PhoneGap, and I'd _assume_ be tied to the Application name (since there is no domain). My gut says it should be more secure - but I wouldn't put any money on it.
Please help, i need to find that database
thank you
So your saying that when you work with a db, it shows notes being saved, but when you restart the app you see no notes?
i am able to add notes on the graphic interface, and when in restart the app i do find the saved notes, but the problem is that i can't find the database(i want to see, and consult the saved data)
Thank you .
Thank you :)
Web Console(544): Uncaught TypeError: Cannot call method 'transaction' of undefined at file:///android_asset/www/js/main.js:56
db = openDatabase("testdata.db","1.0","Apps Sample Database",5*1024*1024);
db.transaction(function(tx){
tx.executeSql("select * from test",[],function(t,res){
var len=res.rows.length;
for(var i=0;i<len;i++){
$('#select-choice-b').append('<option val='+res.rows.item(i)['a']+"'>"+res.rows.item(i)['b']+"</option>");
}
}, function(tx,error)
{
alert("database error:" +error.message);
});
here testdata.db is altready ctreated and have table test with data in it.
Thanks in Advance
A great thanks in advance
FIrst off - are you sure your PHP code is right? I'd test that without PhoneGap. You need a PHP script that can respond to remote requests and return database information in JSON format. You can do this w/o touching PhoneGap at all and it will make testing easier.
At that point, you switch back to your PhoneGap app and see if the Ajax calls work now.
Any ideas why this could be happening?
Do i have to write this:
var db = window.openDatabase("DDB", "1.0", "DDB", 200000);
everytime i want to do a query to the database?
Because when i try to do a query from another html i got nothing I think
Thanks
$("#editPage").live("pageshow", function() {
var loc = window.location.hash;
was not retrieving the url parameter. I found another of your blogs (http://www.raymondcamden.com/index.cfm/2012/2/24/G...) which used the data attribute and that fixed the code for me. Just change the var loc line.
$("#editPage").live("pageshow", function() {
var loc = $(this).data("url");
Perhaps this warrants an update to the code?
Thank you to reply me...
I am just curious as to will it affect performance if we have to execute lots of queries in the table and get their results on app initialization.
If is it so, is there a better way to counter that.
I am looking at SQLLite, database.com and PhoneGap storage. Trying to figure out which one suits the best when size of database might cause issues.
So, if something on same lines have to be thought for Phonegap, either we will create a DB with those 1000 entries at some point in our application or fetch from already created DB. (Hope I'm assuming right).
In case if we are loading wouldn't that slow down the mobile app for user, else we are getting from DB, do you have any suggestions on which one works best.
My apologies for the long comment.
I will use that plugin to work on some demo examples and will see if I could update DB based on user inputs accordingly...
i am new to phonegap. i am using phonegap's cloud build. As u suggest i went through docs about storage. I just want to know, how to give permissions and get started with cloud build ? One Example will be fine.
https://build.phonegap.com/docs/start