Abstracting databases access in Titanium Mobile

7,552 views

Published on

The slides of my conference "Abstracting databases access in Titanium Mobile" at codestrong 2011.

Published in: Technology, Education
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
7,552
On SlideShare
0
From Embeds
0
Number of Embeds
2,087
Actions
Shares
0
Downloads
92
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

Abstracting databases access in Titanium Mobile

  1. 1. Abstracting databases access in Titanium Mobile Xavier Lacot – September 2011
  2. 2. Hello My name is Xavier Lacot ■ I live in Paris ■ I work at Clever Age, as director of the Expertise Center (http://www.clever-age.com/) ■ Open Source convinced and contributor ■ Titanium enthusiast and developer since 2009 ■ Web Frameworks expert ■ Vice Pr. of the French PHP Users Association (afup.org) ■ http://twitter.com/xavierlacotCodeStrong - Abstracting databases access in Titanium Mobile 2Xavier Lacot | September 2011
  3. 3. Summary 1. Using databases in Titanium Mobile applications 2. Who said "pain"? 3. The ORM concept 4. Various js ORMs available ■ Titanium Mobile compatibility chart 5. A focus on joli.js ■ Main use ■ Joli API extensionCodeStrong - Abstracting databases access in Titanium Mobile 3Xavier Lacot | September 2011
  4. 4. Using databases in Titanium Mobile applications ■ Titanium provides a complete Database API : ■ Titanium.Database ■ Titanium.Database.DB ■ Titanium.Database.ResultSet ■ Access to SQLite databases ■ The way to go when manipulating data in mobile applications!CodeStrong - Abstracting databases access in Titanium Mobile 4Xavier Lacot | September 2011
  5. 5. Databases are very common in mobile applications ■ Traveling guides (non-connected mode); ■ News apps, ■ Todo lists, ■ Etc.CodeStrong - Abstracting databases access in Titanium Mobile 5Xavier Lacot | September 2011
  6. 6. Using databases in Titanium Mobile applications // create a connection var db = Titanium.Database.open(database_name); // execute a SQL query var rows = db.execute( SELECT short_url FROM urls WHERE long_url = ?, Longurl ); // get a result if (rows.isValidRow() && rows.fieldByName(short_url)) { result = rows.fieldByName(short_url); } // close the resultset rows.close(); // close the database connection db.close();CodeStrong - Abstracting databases access in Titanium Mobile 6Xavier Lacot | September 2011
  7. 7. Using databases in Titanium Mobile applications ■ Some details to care to: ■ Never forget to close() resultsets, or: ■ you will get memory leaks; ■ The app will unexpectedly close ■ You will have to accept the mix of “view code” and “database code”... javascript and SQL in the same code pages...CodeStrong - Abstracting databases access in Titanium Mobile 7Xavier Lacot | September 2011
  8. 8. Summary 1. Using databases in Titanium Mobile applications 2. Who said "pain"? 3. The ORM concept 4. Various js ORMs available ■ Titanium Mobile compatibility chart 5. A focus on joli.js ■ Main use ■ Joli API extensionCodeStrong - Abstracting databases access in Titanium Mobile 8Xavier Lacot | September 2011
  9. 9. Who said "pain"? ■ Titanium.Database is ok for a few requests ■ It is limited in large scale applications: ■ Imagine a 10 tables model, each with 20 fields ■ Some questions: ■ Why write SQL queries yourself? ■ How to ensure data consistency / related entries retrieval? ■ How to deal with database migrations in your app? ■ How to avoid writing again and again the same queries?CodeStrong - Abstracting databases access in Titanium Mobile 9Xavier Lacot | September 2011
  10. 10. A pain-in-the-ass sample ■ Remove an item from an ordered list ■ Remove the item from the database ■ Update the other items positions // add delete event listener tableview.addEventListener(delete, function(e) { var db = Titanium.Database.open(database_name); // delete the item db.execute( DELETE FROM short_url WHERE id = ?, e.row.children[0].text );CodeStrong - Abstracting databases access in Titanium Mobile 10Xavier Lacot | September 2011
  11. 11. A pain-in-the-ass sample ... // update the other items positions var rows = db.execute(SELECT * FROM short_url ORDER BY position ASC); var position = 1; while (rows.isValidRow()) { db.execute( UPDATE short_url SET position = ? WHERE id = ?, position, rows.fieldByName(id) ); position++; rows.next(); } // be a good boy rows.close(); db.close(); });CodeStrong - Abstracting databases access in Titanium Mobile 11Xavier Lacot | September 2011
  12. 12. CodeStrong - Abstracting databases access in Titanium Mobile 12Xavier Lacot | September 2011
  13. 13. A pain-in-the-ass sample ■ Wait oh wait ■ Our business-code is cluttered with database manipulation code ■ Why not simply write: // add delete event listener tableview.addEventListener(delete, function(e) { // assume short_url is an object which represents the short_url table short_url.get(e.row.children[0].text).remove(); };CodeStrong - Abstracting databases access in Titanium Mobile 13Xavier Lacot | September 2011
  14. 14. Just to convince you... ■ A todo-list application ■ Only display, count, get stats about the tasks of the currently selected category ■ Will you always write the « WHERE category_id = 12 » condition? ■ A better idea: category.get(12).listArticles(); category.get(12).countArticles(); // etcCodeStrong - Abstracting databases access in Titanium Mobile 14Xavier Lacot | September 2011
  15. 15. Summary 1. Using databases in Titanium Mobile applications 2. Who said "pain"? 3. The ORM concept 4. Various js ORMs available ■ Titanium Mobile compatibility chart 5. A focus on joli.js ■ Main use ■ Joli API extensionCodeStrong - Abstracting databases access in Titanium Mobile 15Xavier Lacot | September 2011
  16. 16. What is an « ORM »? ■ Object-Relational Mapper ■ Data access and manipulation abstraction ■ Classes represent tables, objects represent their content table Human // say Human is a mapping class id integer var john = new Human(); lastname text john.set(lastname, Doe); firstname text john.set(firstname, John); city_id integer born_at timestamp // persist it john.save(); is_alive boolean dead_at timestampCodeStrong - Abstracting databases access in Titanium Mobile 16Xavier Lacot | September 2011
  17. 17. Goals of an ORM ■ Manipulate records ■ Never create or delete a record manually ■ Use behaviors (timestampable, taggable, etc.) ■ Clean user entries ■ Execute queries ■ Abstract queries as objects ■ Pass it to several methods ■ Create your data model and manage it with migrationsCodeStrong - Abstracting databases access in Titanium Mobile 17Xavier Lacot | September 2011
  18. 18. Summary 1. Using databases in Titanium Mobile applications 2. Who said "pain"? 3. The ORM concept 4. Various js ORMs available ■ Titanium Mobile compatibility chart 5. A focus on joli.js ■ Main use ■ Joli API extensionCodeStrong - Abstracting databases access in Titanium Mobile 18Xavier Lacot | September 2011
  19. 19. ORMs in javascript ■ There are lots of javascript ORMs ■ Suited for various Database access APIs ■ Browsers ■ Node ■ Titanium ■ Etc. ■ Not every is convenient for Titanium ■ Leaks, incompatibility, not tested, etc. ■ Not using Titanium.databaseCodeStrong - Abstracting databases access in Titanium Mobile 19Xavier Lacot | September 2011
  20. 20. Some of them, designed for Titanium ■ ActiveJS Titanium fork - https://github.com/sr3d/activejs-1584174 ■ AppceleratorRecord - https://github.com/wibblz/AppceleratorRecord ■ JazzRecord - http://www.jazzrecord.org/ ■ TiStore - https://github.com/jcfischer/TiStore ■ yORM - https://github.com/segun/yORM ■ Joli.js - https://github.com/xavierlacot/joli.js ■ Maybe others? … Thats a nice list!CodeStrong - Abstracting databases access in Titanium Mobile 20Xavier Lacot | September 2011
  21. 21. ORMs chartCodeStrong - Abstracting databases access in Titanium Mobile 21Xavier Lacot | September 2011
  22. 22. Summary 1. Using databases in Titanium Mobile applications 2. Who said "pain"? 3. The ORM concept 4. Various js ORMs available ■ Titanium Mobile compatibility chart 5. A focus on joli.js ■ Main use ■ Joli API extensionCodeStrong - Abstracting databases access in Titanium Mobile 22Xavier Lacot | September 2011
  23. 23. Joli.js ■ Why joli.js ■ I could not find what I was looking for in the other ORMs ■ I wanted an abstract query API ■ I wanted something short, simple and efficient ■ Some facts ■ Much inspired by JazzRecord (js) and Doctrine (PHP) ■ First release was written in 3 nightsCodeStrong - Abstracting databases access in Titanium Mobile 23Xavier Lacot | September 2011
  24. 24. Features ■ Models container Models container ■ Models declaration Models ■ Abstract query language Migrations ■ Record lifecycle management ■ Performance analysis Records ■ Extensible Query engine ■ All this in a single ~850 lines file! joli.js Mirakl Coeur Mirakl CoeurCodeStrong - Abstracting databases access in Titanium Mobile 24Xavier Lacot | September 2011
  25. 25. Models container Models container ■ Easy access to the model classes ■ get() Models ■ has() Migrations ■ Etc. Records Query engine ■ Able to launch the migrations joli.js Mirakl Coeur Mirakl CoeurCodeStrong - Abstracting databases access in Titanium Mobile 25Xavier Lacot | September 2011
  26. 26. Models Models container ■ Models represent the tables ■ Model declaration Models ■ Tables creation Migrations ■ Mass-records management ■ Fast selection methods Records (aka « Magic getters ») Query engine joli.js Mirakl Coeur Mirakl CoeurCodeStrong - Abstracting databases access in Titanium Mobile 26Xavier Lacot | September 2011
  27. 27. Declaring a model ■ Include joli.js ■ Declare a connection to your database: joli.connection = new joli.Connection(your_database_name); ■ Describe the model var city = new joli.model({ table: city, columns: { id: INTEGER, name: TEXT, description: TEXT } }); ■ call joli.models.initialize();CodeStrong - Abstracting databases access in Titanium Mobile 27Xavier Lacot | September 2011
  28. 28. Declaring a model ■ Several models? Put them in a bag! var models = (function() { var m = {}; m.human = new joli.model({ table: human, columns: { id: INTEGER PRIMARY KEY AUTOINCREMENT, city_id: INTEGER, first_name: TEXT, last_name: TEXT } }); m.city = new joli.model({ table: city, columns: { id: INTEGER PRIMARY KEY AUTOINCREMENT, name: TEXT } }); return m; })();CodeStrong - Abstracting databases access in Titanium Mobile 28Xavier Lacot | September 2011
  29. 29. table- and object- methods var human = new joli.model({ table: human, columns: { ... }, methods: { countIn: function(cityName) { // do something } }, objectMethods: { moveTo: function(newCityName) { // do something } } }); // use a table-method var habitantsCount = human.countIn(San Francisco); // use an object-method john.moveTo(Paris);CodeStrong - Abstracting databases access in Titanium Mobile 29Xavier Lacot | September 2011
  30. 30. Mass records management var table = models.human; table.truncate(); // remove all humans table.deleteRecords([1, 7, 12]); // remove some records table.exists(118); // test existance, based on "id" // count entities var allCount = table.count(); var DoesCount = table.count({ where: { last_name = ?: Doe, age >= ?: 21 } }); // get all the ones matching criterions var Does = table.all({ where: { last_name = ?: Doe, age >= ?: 21 }, limit: 12 });CodeStrong - Abstracting databases access in Titanium Mobile 30Xavier Lacot | September 2011
  31. 31. Magic getters ■ Goal: Have an easy way to select the records of one table matching a given criteria. ■ findOneById() ■ findOneBy() ■ findBy() var table = models.human; // returns all the inhabitants of the city n°12 var parisians = table.findBy(city_id, 12); // returns one "human" record only (not sorted) var michel = table.findOneBy(first_name, Michel); // returns the human of id "118" var human = table.findOneById(118);CodeStrong - Abstracting databases access in Titanium Mobile 31Xavier Lacot | September 2011
  32. 32. Migrations Models container ■ Update the database layout when updating the application Models ■ Allows to run other operations (callbacks available) Migrations Records Query engine joli.js Mirakl Coeur Mirakl CoeurCodeStrong - Abstracting databases access in Titanium Mobile 32Xavier Lacot | September 2011
  33. 33. Records Models container ■ Records are objects related to a row in the database Models ■ Record creation ■ Record access Migrations ■ Record update Records Query engine ■ Records can be used even while not persisted joli.js Mirakl Coeur Mirakl CoeurCodeStrong - Abstracting databases access in Titanium Mobile 33Xavier Lacot | September 2011
  34. 34. Create new records // first method var john = models.human.newRecord({ first_name: John, last_name: Doe }); // second method var john = new joli.record(models.human); john.fromArray({ first_name: John, last_name: Doe }); // third method var john = new joli.record(models.human); john.set(first_name, John); john.set(last_name, Doe);CodeStrong - Abstracting databases access in Titanium Mobile 34Xavier Lacot | September 2011
  35. 35. Manipulate records // persist a record john.save(); // destroy it john.destroy(); // get a property var name = john.get(last_name); // export to an array var johnArray = john.toArray(); var json = JSON.stringify(johnArray); // {"id":"110","lastname":"Doe","firstname":"John","company_name":"ACME"}CodeStrong - Abstracting databases access in Titanium Mobile 35Xavier Lacot | September 2011
  36. 36. Query engine Models container ■ Abstract the way queries are run against the database Models ■ Stop writing SQL Migrations ■ Use chained method calls « à la jQuery » Records ■ Have hydratation facilities Query engine joli.js Mirakl Coeur Mirakl CoeurCodeStrong - Abstracting databases access in Titanium Mobile 36Xavier Lacot | September 2011
  37. 37. Querying the model ■ No more SQL queries ■ Lets introduce an OOP querying model ■ Queries are objects ■ They can be execute() d // create the query object var q = new joli.query() .select() .from(human) .where(last_name = ?, Doe); // lets execute it var humans = q.execute();CodeStrong - Abstracting databases access in Titanium Mobile 37Xavier Lacot | September 2011
  38. 38. A complete SQL-like vocabulary ■ : Several methods for building queries: ■ count() ■ set() ■ destroy() ■ update() ■ from() ■ values() ■ groupBy() ■ where() ■ insertInto() ■ whereIn() ■ join() ■ limit() ■ order()CodeStrong - Abstracting databases access in Titanium Mobile 38Xavier Lacot | September 2011
  39. 39. Progressive query construction api.getActiveQuery = function(q) { if (!q) { ■ Queries as objects are q = new joli.query() .from(news); easy to handle } q.where(active = ?, true); ■ No matter the order in return q; }; which you call the api.getLastPublished = function() { query methods! return api .getActiveQuery() .limit(1) .orderBy(created_at desc) .execute(); } api.getPublished = function() { return api .getActiveQuery() .orderBy(created_at desc) .execute(); }CodeStrong - Abstracting databases access in Titanium Mobile 39Xavier Lacot | September 2011
  40. 40. Lets talk about hydration ■ Calling execute() will: ■ Build the query string; ■ Send it to joli.Connection() for its execution; ■ And create a bunch of record objects (one per result). ■ This last step is called « hydration » ■ It can cost time. A lot. ■ Joli.js offers a way to hydrate plain arrays, not complete joli.js records.CodeStrong - Abstracting databases access in Titanium Mobile 40Xavier Lacot | September 2011
  41. 41. Lets talk about hydratation var people = new joli.query() .select() .from(people) .execute(); // people is an array of objects var people = new joli.query() .select() .from(people) .execute(array); // people is a simple plain array ■ An ORM as a cost, sure, but you can make it invisible to the user ■ Save you app, take care to the performancesCodeStrong - Abstracting databases access in Titanium Mobile 41Xavier Lacot | September 2011
  42. 42. Querying useful methods ■ getSqlQuery() returns the string that will be generated when executing the query var q = new joli.query() .select() .from(view_count) .where(nb_views between ? And ?, [1000, 2000]); var queryString = q.getSqlQuery(); // select * from view_count where nb_views between "1000" and "2000" ■ All the queries go through joli.Connection.execute(). Possibility to log things here and see what is happening.CodeStrong - Abstracting databases access in Titanium Mobile 42Xavier Lacot | September 2011
  43. 43. Unit tests ■ Joli.js is unit-tested using titanium-jasmine ■ 90+ tests and growing ■ See https://github.com/xavierlacot/joli.js-demo for the test suiteCodeStrong - Abstracting databases access in Titanium Mobile 43Xavier Lacot | September 2011
  44. 44. Summary 1. Using databases in Titanium Mobile applications 2. Who said "pain"? 3. The ORM concept 4. Various js ORMs available ■ Titanium Mobile compatibility chart 5. A focus on joli.js ■ Main use ■ Joli API extensionCodeStrong - Abstracting databases access in Titanium Mobile 44Xavier Lacot | September 2011
  45. 45. Joli API extension ■ We often need to synchronize data from/to the Web ■ Case sample : an online address book ■ We want the contacts to be available on the phone even when not connected ■ The contacts list must also be available online ■ Here comes joli.api.js, the little brother to joli.jsCodeStrong - Abstracting databases access in Titanium Mobile 45Xavier Lacot | September 2011
  46. 46. Joli API extension ■ joli.api.js is a wrapper to joli.js, which makes synchronization to REST web services easy joli.js joli.js REST Web joli.api.js joli.api.js Service Mobile application Mobile application ■ All CRUD operations are available : GET / POST / PUT / DELETECodeStrong - Abstracting databases access in Titanium Mobile 46Xavier Lacot | September 2011
  47. 47. Lets have a small demo ■ A Titanium-powered synchronized AddressBook ■ Code will be available at https://github.com/xavierlacot/joli.api.js-app-demo ■ Uses REST APIs built in PHP with the Symfony frameworkCodeStrong - Abstracting databases access in Titanium Mobile 47Xavier Lacot | September 2011
  48. 48. API synchronized model declaration joli.apimodel var people = new joli.apimodel({ table: people, columns: { id: INTEGER PRIMARY KEY AUTOINCREMENT, firstname: TEXT, lastname: TEXT, company_name: TEXT, email: TEXT, phone: TEXT, picture_url: TEXT }, updateTime: 86400, url: http://local.example.com/api/people.json }); The REST endpoint urlCodeStrong - Abstracting databases access in Titanium Mobile 48Xavier Lacot | September 2011
  49. 49. Using the API ■ Minor changes compared to joli.js The exact same method // selects from the database // if no result and the updateTime is gone, checks the API var peoples = joli.models.get(people).all({ order: [lastname asc, firstname asc] }); // creates the record and saves it to the REST endpoint joli.models.get(people) .newRecord(values, true) .save(); Should the record beCodeStrong - Abstracting databases access in Titanium Mobile synchronized? 49Xavier Lacot | September 2011
  50. 50. This is Free and Open Source Software... ■ All the code is here : ■ joli.js - https://github.com/xavierlacot/joli.js ■ joli.api.js - https://github.com/xavierlacot/joli.api.js ■ joli.js test suite - https://github.com/xavierlacot/joli.js-demo ■ joli.api.js demo application - https://github.com/xavierlacot/joli.api.js-app-demoCodeStrong - Abstracting databases access in Titanium Mobile 50Xavier Lacot | September 2011
  51. 51. Lets have a small demo ■ This app was built completely while I was in the plane. Less than 4 hours coding! // persist the values of the form button.addEventListener(click, function() { // extractValues() builds an associative array of the form values save(extractValues(container)); win.close(); }); var save = function(values) { joli.models.get(people).newRecord(values, true).save(); }; [INFO] POST request to url http://local.example.com/api/people.json [INFO] Received from the service: [INFO] {"id":"111","lastname":"Lacot","firstname":"Xavier", ...} [INFO] 1 new record(s), 0 record(s) updated. [DEBUG] fire app event: joli.records.savedCodeStrong - Abstracting databases access in Titanium Mobile 51Xavier Lacot | September 2011
  52. 52. Roadmap and whishlist... ■ Joli.js: ■ Abstract the configuration ■ Logging enabled or not, default hydration model ■ Easy support for several databases ■ Improve migrations, add more unit tests ■ Joli.api.js ■ Support for all the HTTP methods ■ Make it possible to map the Data model to different REST services formats Keep all this fun, short and efficientCodeStrong - Abstracting databases access in Titanium Mobile 52Xavier Lacot | September 2011
  53. 53. CodeStrong - Abstracting databases access in Titanium Mobile 53Xavier Lacot | September 2011

×