I am developing a mobile application using phonegap that store some data into the local database (sqlite DB).
I need to know if the database exist or not, and that to determine which process need to execute.
var database = window.openDatabase("my_db", "1.0", "sample DB", 30000);
if (check_db_exist()) {
process_1();
}
else
{
process_2();
}
I needed to do something similar, I needed to check if the application had a db already created (legacy DB) and if so export all the data to the new db (new and improved DB) and then delete this DB.
Background Info: I was moving from simple keyvalue tables to complex relational DB with cascades etc.
function onDeviceReady() {
// CHECK IF LEGACY DATABASE EXISTS. IF DOES EXPORT EXISTING DATA TO NEW DB THEN DELETE LEGACY
window.resolveLocalFileSystemURL(cordova.file.applicationStorageDirectory + "/databases/<DatabaseName>.db", exportToNewDB, setupDB);
}
Note: If file exists (success), then we need to do our export code in here, then delete file so this method will always fail. If file doesn't exist - user has already exported to new DB or they our new user and never had legacy DB.
// Fail Method
function setupDB() {
newDB = window.sqlitePlugin.openDatabase({ name: "<NewImprovedDBName>.db" });
newDB.transaction(sql.initDB, sql.errorCallback, sql.successCallBack);
}
// Success Method
function exportToNewDB() {
db = window.sqlitePlugin.openDatabase({ name: "<LegacyDBName>.db" });
db.transaction(function (tx) {
setupDB();
// Insert Export code Here
// Delete DB
window.sqlitePlugin.deleteDatabase("<LegacyDBName>.db", sqlSuccess, sqlFail);
}, sqlFail);
}
Answer to your Question:
window.resolveLocalFileSystemURL(cordova.file.applicationStorageDirectory + "/databases/my_db.db", process_1, process_2);
The best way for determining if the DB exists or not is to check if the file that represents it exists. This is a simple IO operation, like the following example:
string path = Path.Combine(ApplicationData.Current.LocalFolder.Path, databaseName);
if (File.Exists(path))
{
//your code here
}
I don't think that you can check for the existence of the DB directly. I've researched and haven't found a way to do it using a simple function call. However, this seems to be a popular request, and here's a workaround:
var db = window.openDatabase("my.db", "1", "Demo", -1);
db.transaction(function (tx) {
/*I don't think that there is a way to check if a database exists.
As I think that the openDatabase call above will just create
a missing DB. Here is a second-best way to do it - there
is a way to check to see if a table exists. Just pick a table that
your DB should have and query it. If you get an ERROR, then you
can assume your DB is missing and you will need to create it. */
console.log("Trying to get the test data");
tx.executeSql("select * from test_table", [], function (tx, res) {
var len = res.rows.length, i;
for (i = 0; i < len; i++) {
console.log(res.rows.item(i).data);
}
}, function (err) {
console.log("Error selecting: " + err);
//NOW we need to create the DB and populate it
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text)');
tx.executeSql('INSERT INTO test_table (data) VALUES("test data one")');
tx.executeSql('INSERT INTO test_table (data) VALUES("test data two")');
//now test select
tx.executeSql("select * from test_table", [], function (tx, res) {
var len = res.rows.length, i;
for (i = 0; i < len; i++) {
console.log(res.rows.item(i).data);
}
});
//now clean up so we can test this again
tx.executeSql('DROP TABLE IF EXISTS test_table', [], function (tx, res) {
console.log("dropped table");
}, function (err) {
console.log("Error dropping table: " + err);
});
});
});
As you can see, the error function during the first query will create and populate the DB. It's using exception handling for program logic flow, but it works!
Related
I am trying to populate Arrays dynamically from local storage SQLite Database Tables when a controller is loaded.
I have several Arrays defined as follows (small sample shown):
$scope.myArray1 = [];
$scope.myArray2 = [];
I then create an array with the table name/array name associateion e.g. my_table_1 should be queried and then be written to $scope.myArray1. This looks as below (small sample shown):
$scope.tableArrayAssociation = [{
tablename: "my_table_1",
arrayname: "myArray1"
}, {
tablename: "my_table_1",
arrayname: "myArray2"
}];
To do this I have a function that calls a SQLite local storage database table and reads values from it.For my function I pass the table name to query and array to write the data to as function parameters.
I can query the required table no problem from the passed parameter, but when I try and insert into the array I get an error. Below is said function.
onDeviceReady
function onDeviceReady()
{
queryLocalStorageTablesAndBuildArrays($scope.tableArrayAssociation);
}
Loop through each item, query the table and push to array.
function queryLocalStorageTablesAndBuildArrays(tableArrayAssociation) {
for (var i = 0; i < $scope.tableArrayAssociation.length; i++) {
queryTable($scope.tableArrayAssociation[i].tablename, $scope.tableArrayAssociation[i].arrayname);
}
function queryTable(tableName, arrayName) {
var sql = 'SELECT * FROM ' + tableName + ''; // OK
db.transaction(function (tx) {
tx.executeSql(sql, [],
(function (arrayName) {
return function (tx, results) {
querySuccess(tx, results, arrayName);
};
})(arrayName), errorCB); // OK
});
}
function querySuccess(tx, results, arrayName) {
var len = results.rows.length; // OK
for (var i = 0; i < len; i++) {
$scope[arrayName].push(results.rows.item(i)); // Not working here
arrayName.push(results.rows.item(i)); // Also tried this, but arrayName.push is not a function error thrown
}
}
arrayName.push(...) not working because I am just trying to push to a name (String) and not an actual Array. Showing a log for $scope[arrayName] shows it is an object but that the push didnt work - value = undefined.
Can anyone please suggest a solution to this? How do I assign the queried tables values to its corresponding array to populate values in my view?
I am using Sails v0.11 and am developing an standalone importer script in order to import data to mongoDB and - that is now the not-working part - build the associations between the models.
For this process I introduced temporary helper properties in the models in order to find the associated records and replace them by in real MongoDB _ids.
The script starts Sails in order to be able use its features (waterline, etc.):
var app = Sails();
app.load({
hooks: { grunt: false },
log: { level: 'warn' }
}, function sailsReady(err){
processUsers() finds all users and their _ids and iterates over them to invoke a second function addOrgsToOneUser()
var processUsers = function() {
// Iterate through all users in order to retrieve their _ids and
app.models['user'].native(function(err, collection) {
collection.find({}, projectionOrgInUser).toArray(function (err, users) {
Async.eachSeries(users, function (user, next){
// prepare userInOrgs
whereUserInOrg = { orgId: { $in: userInOrgs } };
//This is invoking
addOrgsToOneUser(user, whereUserInOrg);
next();
}, function afterwards (err) {
if (err) {
console.error('Import failed, error details:\n',err);
return process.exit(1);
}
console.log("done");
return process.exit(0); // This returns too early, not executing the addOrgsToOneUser
});
});
});
};
addOrgsToOneUser() finds all orgs belonging to THIS user and updates then the orgs array property of THIS user
var addOrgsToOneUser = function(user, whereUserInOrg) {
var projectionUserInOrg = "...";
// Find all orgs that this user is associated to and store it in inOrgs
app.models['org'].native(function(err, collection) {
collection.find(whereUserInOrg, projectionUserInOrg).toArray(function (err, orgs) {
// prepare inOrgs which is needed for updating
//update user to have an updated orgs array based on inOrgs.
app.models['user'].update({'id' : user._id.toString()}, {'orgs': inOrgs}).exec(function afterwards(err, updated){
console.log('Updated user ' + user._id.toString() + ' to be in their orgs');
});
});
});
}
Problem:
Process.exit(0) is called before the query/update of saddOrgsToOneUser() has completed. It behaves as expected if saddOrgsToOneUser() contains just a console.log for instance, but queries are triggered ansynchronously of course.
In case I comment out Process.exit(0), the script never stops, but the queries are executed as intented.
As the script will have further nested queries, I need a better approach to this as manually kill this script ...
How is nesting queries and iterating over their results done properly?
Thank you very much,
Manuel
addOrgsToOneUser is asynchronous. next() needs to be called after everything is done inside addOrgsToOneUser. The way I would do it is to pass in a callback (next) and call it when everything is done. So the call is
addOrgsToOneUser(user, whereUserInOrg, next);
and the addOrgsToOneUser will have an extra argument:
var addOrgsToOneUser = function(user, whereUserInOrg, callback) {
var projectionUserInOrg = "...";
// Find all orgs that this user is associated to and store it in inOrgs
app.models['org'].native(function(err, collection) {
collection.find(whereUserInOrg, projectionUserInOrg).toArray(function (err, orgs) {
// prepare inOrgs which is needed for updating
//update user to have an updated orgs array based on inOrgs.
app.models['user'].update({'id' : user._id.toString()}, {'orgs': inOrgs}).exec(function afterwards(err, updated){
console.log('Updated user ' + user._id.toString() + ' to be in their orgs');
callback(); // your original next() is called here
});
});
});
}
I am using sockets with mongodb, for a user who is trying to create a new name, I need to check all the models in the database to see if it exists.
I am doing it all wrong, basically I am trying to do something like this.
var allUsers = [];
models.Message.find({}, function(err, data) {
for(var i=0; i < data.length; i++) {
allUsers.push(data[i].username);
}
});
console.log(allUsers)
I'm sitting here struggling even getting the allUsers out of the function, and I am thinking this is not even the best way to do this. With allUsers I was just going to check to see if the new username existed in the array.
So to futher extend what I am doing here is some socket.io code. I was going to run some validation like this if I could get the allUsers to work.
socket.on('new user', function (data, callback) {
if(data in allUsers) {
callback(false);
} else {
callback(true);
socket.userName = data;
socket.connected = true;
users[socket.userName] = socket;
io.sockets.emit('user name', {usernames: users[socket.userName].userName, connected: users[socket.userName].connected});
}
});
But without it working, this is no good. So my question is with what I have provided (socket.io, mongodb) how do I get all the models and validate if a new user which is passed in data exists in the database?
models.Message.find is async, the result of the async operation is only available when the async operation has finished.so console.log(allUsers) will always yield an empty array.
should be something like (pseudo js code):
socket.on('new user', function (data, callback) {
models.User.findOne({username:data.username},function(err,user){
if(err){/*deal with error here */}
else if(user){/*username already taken
respond with appropriate socket message here */
socket.emit('user name already taken',{somemessage});
}
else{/* user with username not found */
/*create new user into database then emit socket message */
var user = new models.User(data);
user.save(function(err,user){
socket.emit('user name',{somemessage});
})
}
});
});
I used sqlite to populate a DB with some Tables in it.
I made a function at another javascript page that executes the database & selects some values from the table. The function is called at $(document).ready().
Javascript:
//DB Population
function onDeviceReady() {
var db = window.openDatabase("Database", "1.0", "SqliteTrial", 20000);
db.transaction(populateDB, errorCB, successCB);
}
function populateDB(tx) {
tx.executeSql('DROP TABLE IF EXISTS Subjects');
tx.executeSql('CREATE TABLE IF NOT EXISTS Subjects (id unique, subjectname)');
tx.executeSql('INSERT INTO Subjects (id, subjectname) VALUES (1, "Math")');
tx.executeSql('INSERT INTO Subjects (id, subjectname) VALUES (2, "Science")');
}
function GetSubjectsFromDB()
{
console.log("");
tx.executeSql('SELECT * FROM Subjects', [], queryNSuccess, errorCB);
}
function queryNSuccess(tx, results) {
alert("Query Success");
console.log("Returned rows = " + results.rows.length);
if (!results.rowsAffected) {
console.log('No rows affected!');
return false;
}
console.log("Last inserted row ID = " + results.insertId);
}
function errorCB(err) {
alert("Error processing SQL: "+err.code);
}
Is there some problem with this line?
tx.executeSql('SELECT * FROM Subjects', [], queryNSuccess, errorCB);
The queryNSuccess isn't called, neither is the errorCB so I don't know what's wrong.
This is how I call it at another page:
Javascript:
$(document).ready(function () {
DisplayData();
GetSubjectsFromDB(tx);
});
No, it doesn't work like that. tx variable is actually a parameter that will be sent into the specified callback function by db.transaction method. So you're probably want to do this instead:
$(document).ready(function () {
...
db.transaction(GetSubjectsFromDB);
});
... and rewrite this function definition as...
function GetSubjectsFromDB(tx) { ... something to do with tx ... }
But there's another problem actually, as I see it. Your db variable, which stores the connection handle (created by window.openDatabase call) is local to onDeviceReady function - in other words, it's not visible outside of this function.
The easiest way to solve this is to define this variable at the Global context:
var dbh; // the SQLite connection handle
function onDeviceReady() { ... dbh = window.openDatabase ... }
function GetSubjects() { ... dbh.transaction(getSubjectsFromDb) ... }
function getSubjectsFromDb(tx) { ... tx.executeSql(...) ... }
Here's a great presentation describing general usage of WebSQL DB. But I'd also like to add that WebSQL DB API is considered deprecated; it's recommended to use IndexedDB instead. Here's something to read about it.
I am trying to store some data in a database which i create using phonegap like
document.addEventListener("deviceready", onDeviceReady, false);
function populateDB(tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS DEMO (id unique, data)');
tx.executeSql('INSERT INTO DEMO (id, data) VALUES (1, "First row")');
tx.executeSql('INSERT INTO DEMO (id, data) VALUES (2, "Second row")');
}
// Query the database
//
function queryDB(tx) {
tx.executeSql('SELECT * FROM DEMO', [], querySuccess, errorCB);
}
// Query the success callback
//
function querySuccess(tx, results) {
var len = results.rows.length;
console.log("DEMO table: " + len + " rows found.");
for (var i=0; i<len; i++){
console.log("Row = " + i + " ID = " + results.rows.item(i).id + " Data = " + results.rows.item(i).data);
}
}
// Transaction error callback
//
function errorCB(err) {
console.log("Error processing SQL: "+err.code);
}
// Transaction success callback
//
function successCB() {
var db = window.openDatabase("Database", "1.0", "PhoneGap Demo", 200000);
db.transaction(queryDB, errorCB);
}
// PhoneGap is ready
//
function onDeviceReady() {
var db = window.openDatabase("Database", "1.0", "PhoneGap Demo", 200000);
db.transaction(populateDB, errorCB, successCB);
}
But the database is not getting created..Instead it is saving it in /data/data/package-name/app_database/file__0/00000000000000001.db
I want to save the data in /data/data/package-name/database/Database.db
How to do it?
You can't change the directory in which the database is created unless you modify the PhoneGap source code. I guess a better question is why do you want to save the data in /data/data/package-name/database/Database.db instead of /data/data/package-name/app_database/file__0/00000000000000001.db?
Update: Now that I understand the requirement better the only way you are going to be able to do this is to write a plugin for Android that interfaces the JavaScript side to the native Java side. Then you can open and read the DB in Java that your Service has created and populated.
Marc Murphy (CommonsWare) goes over the why in this thread.
See: http://developer.android.com/guide/topics/data/data-storage.html#db and http://developer.android.com/guide/developing/tools/adb.html#sqlite. When creating the database, you can specify the database name and a file name with .db will be created in /data/data/package name/databases/.
Sorry, but the code you have provided doesn't show how the database has been created.
I would suggest that you use LocalStorage instread of sqlite for this purpose.
You can refer to Local storage at : http://docs.phonegap.com/en/1.0.0/phonegap_storage_storage.md.html
Example :
localStorage.setItem("ItemName",yourItem);
localStorage.getItem("ItemName");
It provides a better way to store and retrieve your data easily and effectively
As the documentation states, Phonegap doesn't really use a SQLite database, but a WebSQL implementation or localStorage.
If you want to use native SQLite, have a look at this plugin, the syntax for database operations are nearly the same as for the Storage API of Phonegap.