SlideShare a Scribd company logo
SQLite & ORM Binding - Part I
In this module we will persist data locally so we don’t have to go to the server to fetch data every time. We’ll use SQLite not because it’s the best storage medium but
because it’s something I want to teach.
SQLite Mapping
✦SQLite property objects can be mapped to SQLite tables
✦I also had to moved a couple of properties to getters/
setters as at the time I built the app the API didn’t allow
excluding an entry
✦I abstracted the mapping further by creating my own
persistence class
© Codename One 2017 all rights reserved
I’ll use the automated properties binding API to map objects to SQLite which makes working with SQLite far easier. If I didn’t have that API I might have just skipped over
sqlite altogether as working with SQL is unpleasant and error prone.

In order to get the mapping right I had to move some properties to getter and setters as at the time I originally did this code there was no option to exclude an entry from
storage. This is no longer the case and we can now mark a property for exclusion but changing that would mean changing all the following modules and that’s nor
necessarily something I want to do right now.

I used a local abstraction for all the persistence code to make it more domain specific which is a good practice in general
public class AppStorage {
private static SQLMap smap;
private static Database db;
private static AppStorage instance = new AppStorage();
private EventDispatcher dispatcher = new EventDispatcher();
private AppStorage() {}
public static AppStorage getInstance() {
initStorage();
return instance;
}
private static void initStorage() {
if(smap == null) {
try {
db = Display.getInstance().openOrCreate("RestDb.sql");
smap = SQLMap.create(db);
AppSettings app = new AppSettings();
Dish dish = new Dish();
smap.setPrimaryKey(app, app.id);
smap.setPrimaryKey(dish, dish.id);
smap.setSqlType(dish.price, SQLMap.SqlType.SQL_DOUBLE);
smap.createTable(app);
smap.createTable(dish);
} catch(IOException err) {
Log.e(err);
}
}
}
AppStorage
App storage abstracts the process of saving the objects, so right now it’s implemented using SQLMap but in theory we could replace that. Let’s review this class
public class AppStorage {
private static SQLMap smap;
private static Database db;
private static AppStorage instance = new AppStorage();
private EventDispatcher dispatcher = new EventDispatcher();
private AppStorage() {}
public static AppStorage getInstance() {
initStorage();
return instance;
}
private static void initStorage() {
if(smap == null) {
try {
db = Display.getInstance().openOrCreate("RestDb.sql");
smap = SQLMap.create(db);
AppSettings app = new AppSettings();
Dish dish = new Dish();
smap.setPrimaryKey(app, app.id);
smap.setPrimaryKey(dish, dish.id);
smap.setSqlType(dish.price, SQLMap.SqlType.SQL_DOUBLE);
smap.createTable(app);
smap.createTable(dish);
} catch(IOException err) {
Log.e(err);
}
}
}
AppStorage
The class is implemented as a singleton and initialized on first use
public class AppStorage {
private static SQLMap smap;
private static Database db;
private static AppStorage instance = new AppStorage();
private EventDispatcher dispatcher = new EventDispatcher();
private AppStorage() {}
public static AppStorage getInstance() {
initStorage();
return instance;
}
private static void initStorage() {
if(smap == null) {
try {
db = Display.getInstance().openOrCreate("RestDb.sql");
smap = SQLMap.create(db);
AppSettings app = new AppSettings();
Dish dish = new Dish();
smap.setPrimaryKey(app, app.id);
smap.setPrimaryKey(dish, dish.id);
smap.setSqlType(dish.price, SQLMap.SqlType.SQL_DOUBLE);
smap.createTable(app);
smap.createTable(dish);
} catch(IOException err) {
Log.e(err);
}
}
}
AppStorage
Initialization creates the DB instance that is kept within the application. We create the sql mapping instance to the database. Notice that the sqlite API doesn’t have
separate methods for create or open and does the former implicitly which is pretty convenient.
public class AppStorage {
private static SQLMap smap;
private static Database db;
private static AppStorage instance = new AppStorage();
private EventDispatcher dispatcher = new EventDispatcher();
private AppStorage() {}
public static AppStorage getInstance() {
initStorage();
return instance;
}
private static void initStorage() {
if(smap == null) {
try {
db = Display.getInstance().openOrCreate("RestDb.sql");
smap = SQLMap.create(db);
AppSettings app = new AppSettings();
Dish dish = new Dish();
smap.setPrimaryKey(app, app.id);
smap.setPrimaryKey(dish, dish.id);
smap.setSqlType(dish.price, SQLMap.SqlType.SQL_DOUBLE);
smap.createTable(app);
smap.createTable(dish);
} catch(IOException err) {
Log.e(err);
}
}
}
AppStorage
Next we create the tables if they don’t exist. We create blank object instances for the objects that we need, then define the primary key field.

I also set the column type for price to be double, this is no longer necessary as newer versions of the SQLMap API detect the DoubleProperty and automatically set the
right type. Create table literally issues a create statement, notice that we don’t support alter at this time although this might be introduced in the future.
public void insert(PropertyBusinessObject d) {
try {
smap.insert(d);
} catch(IOException err) {
Log.e(err);
ToastBar.showErrorMessage("Error saving: " + err);
}
}
public void update(PropertyBusinessObject d) {
try {
smap.update(d);
} catch(IOException err) {
Log.e(err);
ToastBar.showErrorMessage("Error updating storage: " + err);
}
}
public void addDeleteListener(ActionListener<ActionEvent> onDelete) {
dispatcher.addListener(onDelete);
}
public void removeDeleteListener(ActionListener<ActionEvent> onDelete) {
dispatcher.removeListener(onDelete);
}
AppStorage
Insert and update are pretty much direct wrappers to the builtin methods that handle the exceptions locally instead of propagating the IOException from the sqlite API
public void insert(PropertyBusinessObject d) {
try {
smap.insert(d);
} catch(IOException err) {
Log.e(err);
ToastBar.showErrorMessage("Error saving: " + err);
}
}
public void update(PropertyBusinessObject d) {
try {
smap.update(d);
} catch(IOException err) {
Log.e(err);
ToastBar.showErrorMessage("Error updating storage: " + err);
}
}
public void addDeleteListener(ActionListener<ActionEvent> onDelete) {
dispatcher.addListener(onDelete);
}
public void removeDeleteListener(ActionListener<ActionEvent> onDelete) {
dispatcher.removeListener(onDelete);
}
AppStorage
The reason for the delete listener is that we want the dish list to automatically remove the dish when it’s deleted by the edit form. This allows us to decouple that code
and get an event from the underlying data model.

We use the event dispatcher class to subscribe and fire an event here, this is a really convenient class when you are building an API
public void delete(PropertyBusinessObject d) {
try {
smap.delete(d);
dispatcher.fireActionEvent(new ActionEvent(d));
} catch(IOException err) {
Log.e(err);
ToastBar.showErrorMessage("Error deleting: " + err);
}
}
public AppSettings fetchAppSettings() {
try {
AppSettings a = new AppSettings();
a.name.set(null);
a.tagline.set(null);
List<PropertyBusinessObject> lp = smap.select(a, null, true, 1000, 0);
if(lp.size() == 0) {
a = new AppSettings();
a.id.set("1");
insert(a);
return a;
}
return (AppSettings)lp.get(0);
} catch(Exception err) {
Log.e(err);
ToastBar.showErrorMessage("Error loading AppSettings: " + err);
return null;
}
AppStorage
Delete serves pretty much the same purpose as update and insert with the exception that it also fires the dispatch event appropriately
public void delete(PropertyBusinessObject d) {
try {
smap.delete(d);
dispatcher.fireActionEvent(new ActionEvent(d));
} catch(IOException err) {
Log.e(err);
ToastBar.showErrorMessage("Error deleting: " + err);
}
}
public AppSettings fetchAppSettings() {
try {
AppSettings a = new AppSettings();
a.name.set(null);
a.tagline.set(null);
List<PropertyBusinessObject> lp = smap.select(a, null, true, 1000, 0);
if(lp.size() == 0) {
a = new AppSettings();
a.id.set("1");
insert(a);
return a;
}
return (AppSettings)lp.get(0);
} catch(Exception err) {
Log.e(err);
ToastBar.showErrorMessage("Error loading AppSettings: " + err);
return null;
}
AppStorage
App Settings is a special object as there is only one of it. So when we issue a select query we should never expect more than one entry. However it’s still a select query…
By default when we invoke the select method if a property value isn’t null it’s added to the where clause. Both name and tagline default to non-null values and so we
need to set them to null. Once that’s done we can just do the select query as usual.
public void delete(PropertyBusinessObject d) {
try {
smap.delete(d);
dispatcher.fireActionEvent(new ActionEvent(d));
} catch(IOException err) {
Log.e(err);
ToastBar.showErrorMessage("Error deleting: " + err);
}
}
public AppSettings fetchAppSettings() {
try {
AppSettings a = new AppSettings();
a.name.set(null);
a.tagline.set(null);
List<PropertyBusinessObject> lp = smap.select(a, null, true, 1000, 0);
if(lp.size() == 0) {
a = new AppSettings();
a.id.set("1");
insert(a);
return a;
}
return (AppSettings)lp.get(0);
} catch(Exception err) {
Log.e(err);
ToastBar.showErrorMessage("Error loading AppSettings: " + err);
return null;
}
AppStorage
If there are no entries we just invoke insert for a new settings object.
public List<PropertyBusinessObject> fetchDishes() {
try {
Dish d = new Dish();
List<PropertyBusinessObject> lp = smap.select(d, d.name, true, 1000, 0);
// workaround for null images
for(PropertyBusinessObject p : lp) {
Dish dd = (Dish)p;
if(dd.getFullSize() == null) {
dd.setFullSize(Resources.getGlobalResources().getImage("food1.jpg"));
}
}
return lp;
} catch(Exception err) {
Log.e(err);
ToastBar.showErrorMessage("Error loading dishes: " + err);
return null;
}
}
AppStorage
Fetching the list of dishes is similar but more standard as we return the full list of dishes. The main point of interest is the use of the name property to indicate the order
by option for the select statement.
public List<PropertyBusinessObject> fetchDishes() {
try {
Dish d = new Dish();
List<PropertyBusinessObject> lp = smap.select(d, d.name, true, 1000, 0);
// workaround for null images
for(PropertyBusinessObject p : lp) {
Dish dd = (Dish)p;
if(dd.getFullSize() == null) {
dd.setFullSize(Resources.getGlobalResources().getImage("food1.jpg"));
}
}
return lp;
} catch(Exception err) {
Log.e(err);
ToastBar.showErrorMessage("Error loading dishes: " + err);
return null;
}
}
AppStorage
Some dishes might not have an image within them as we create them and here we force a placeholder image to prevent such a case. I would recommend that you
always use that strategy and have a good looking placeholder image.

Now that we have the basic persistence object in place lets proceed to integration but first let’s discuss a conceptual issue. Why do we need persistence to begin with?
Local & Remote DB
✦Keeping both a local and remote DB is risky but since
we currently have the assumption of one user per
restaurant it’s lower
✦We don’t really support working offline, this is mostly
meant for faster loading and reduced server load
© Codename One 2017 all rights reserved
Technically we shouldn’t have a local database as all the data is in the server and local data is meaningless. In theory we could enable working offline but that’s not a
current goal and would create many complexities I want to avoid.

There is only one goal for this code and it’s performance. Users and developers often conflate performance with something that has to do with bits and bytes but it’s
really about the decisions we make as developers. Do we go to the server or do we use local data? Do we cache?

In this case it can create a conflict between the local and the remote database. We need to guard against that obviously but once data duplication exists we open up a
risk. This risk is worth it for this level of performance.

More Related Content

Similar to SQLite and ORM Binding - Part 1 - Transcript.pdf

React lecture
React lectureReact lecture
React lecture
Christoffer Noring
 
AngularJS - Services
AngularJS - ServicesAngularJS - Services
AngularJS - Services
Nir Kaufman
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
Ryunosuke SATO
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
Lars Jankowfsky
 
Stop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScriptStop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScript
Ryan Anklam
 
Using the Windows 8 Runtime from C++
Using the Windows 8 Runtime from C++Using the Windows 8 Runtime from C++
SharePoint Conference 2018 - APIs, APIs everywhere!
SharePoint Conference 2018 - APIs, APIs everywhere!SharePoint Conference 2018 - APIs, APIs everywhere!
SharePoint Conference 2018 - APIs, APIs everywhere!
Sébastien Levert
 
Automated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.xAutomated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.x
Tatsuya Maki
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
Jeff Durta
 
Migrating from Flux to Redux. Why and how.
Migrating from Flux to Redux. Why and how.Migrating from Flux to Redux. Why and how.
Migrating from Flux to Redux. Why and how.
Astrails
 
Converting Db Schema Into Uml Classes
Converting Db Schema Into Uml ClassesConverting Db Schema Into Uml Classes
Converting Db Schema Into Uml Classes
Kaniska Mandal
 
SQLite and ORM Binding - Part 2 - Transcript.pdf
SQLite and ORM Binding - Part 2 - Transcript.pdfSQLite and ORM Binding - Part 2 - Transcript.pdf
SQLite and ORM Binding - Part 2 - Transcript.pdf
ShaiAlmog1
 
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptjQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
Guy Royse
 
Finishing the App - Part 1.pdf
Finishing the App - Part 1.pdfFinishing the App - Part 1.pdf
Finishing the App - Part 1.pdf
ShaiAlmog1
 
Androidaop 170105090257
Androidaop 170105090257Androidaop 170105090257
Androidaop 170105090257
newegg
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
Ali Parmaksiz
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
ShriKant Vashishtha
 
Improving android experience for both users and developers
Improving android experience for both users and developersImproving android experience for both users and developers
Improving android experience for both users and developers
Pavel Lahoda
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahoda
Droidcon Berlin
 
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
탑크리에듀(구로디지털단지역3번출구 2분거리)
 

Similar to SQLite and ORM Binding - Part 1 - Transcript.pdf (20)

React lecture
React lectureReact lecture
React lecture
 
AngularJS - Services
AngularJS - ServicesAngularJS - Services
AngularJS - Services
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Stop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScriptStop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScript
 
Using the Windows 8 Runtime from C++
Using the Windows 8 Runtime from C++Using the Windows 8 Runtime from C++
Using the Windows 8 Runtime from C++
 
SharePoint Conference 2018 - APIs, APIs everywhere!
SharePoint Conference 2018 - APIs, APIs everywhere!SharePoint Conference 2018 - APIs, APIs everywhere!
SharePoint Conference 2018 - APIs, APIs everywhere!
 
Automated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.xAutomated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.x
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 
Migrating from Flux to Redux. Why and how.
Migrating from Flux to Redux. Why and how.Migrating from Flux to Redux. Why and how.
Migrating from Flux to Redux. Why and how.
 
Converting Db Schema Into Uml Classes
Converting Db Schema Into Uml ClassesConverting Db Schema Into Uml Classes
Converting Db Schema Into Uml Classes
 
SQLite and ORM Binding - Part 2 - Transcript.pdf
SQLite and ORM Binding - Part 2 - Transcript.pdfSQLite and ORM Binding - Part 2 - Transcript.pdf
SQLite and ORM Binding - Part 2 - Transcript.pdf
 
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptjQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
 
Finishing the App - Part 1.pdf
Finishing the App - Part 1.pdfFinishing the App - Part 1.pdf
Finishing the App - Part 1.pdf
 
Androidaop 170105090257
Androidaop 170105090257Androidaop 170105090257
Androidaop 170105090257
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
 
Improving android experience for both users and developers
Improving android experience for both users and developersImproving android experience for both users and developers
Improving android experience for both users and developers
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahoda
 
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
 

More from ShaiAlmog1

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
ShaiAlmog1
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdf
ShaiAlmog1
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdf
ShaiAlmog1
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdf
ShaiAlmog1
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdf
ShaiAlmog1
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdf
ShaiAlmog1
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdf
ShaiAlmog1
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
ShaiAlmog1
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdf
ShaiAlmog1
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdf
ShaiAlmog1
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdf
ShaiAlmog1
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
ShaiAlmog1
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdf
ShaiAlmog1
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdf
ShaiAlmog1
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdf
ShaiAlmog1
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdf
ShaiAlmog1
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdf
ShaiAlmog1
 
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdf
ShaiAlmog1
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdf
ShaiAlmog1
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdf
ShaiAlmog1
 

More from ShaiAlmog1 (20)

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdf
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdf
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdf
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdf
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdf
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdf
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdf
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdf
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdf
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdf
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdf
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdf
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdf
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdf
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdf
 

Recently uploaded

GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
Neo4j
 
20240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 202420240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 2024
Matthew Sinclair
 
Goodbye Windows 11: Make Way for Nitrux Linux 3.5.0!
Goodbye Windows 11: Make Way for Nitrux Linux 3.5.0!Goodbye Windows 11: Make Way for Nitrux Linux 3.5.0!
Goodbye Windows 11: Make Way for Nitrux Linux 3.5.0!
SOFTTECHHUB
 
Mind map of terminologies used in context of Generative AI
Mind map of terminologies used in context of Generative AIMind map of terminologies used in context of Generative AI
Mind map of terminologies used in context of Generative AI
Kumud Singh
 
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Speck&Tech
 
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
Neo4j
 
AI 101: An Introduction to the Basics and Impact of Artificial Intelligence
AI 101: An Introduction to the Basics and Impact of Artificial IntelligenceAI 101: An Introduction to the Basics and Impact of Artificial Intelligence
AI 101: An Introduction to the Basics and Impact of Artificial Intelligence
IndexBug
 
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success StoryDriving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Safe Software
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
shyamraj55
 
GenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizationsGenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizations
kumardaparthi1024
 
RESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for studentsRESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for students
KAMESHS29
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
Octavian Nadolu
 
Full-RAG: A modern architecture for hyper-personalization
Full-RAG: A modern architecture for hyper-personalizationFull-RAG: A modern architecture for hyper-personalization
Full-RAG: A modern architecture for hyper-personalization
Zilliz
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
Adtran
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1
DianaGray10
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
Kari Kakkonen
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
Zilliz
 
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Malak Abu Hammad
 

Recently uploaded (20)

GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
 
20240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 202420240605 QFM017 Machine Intelligence Reading List May 2024
20240605 QFM017 Machine Intelligence Reading List May 2024
 
Goodbye Windows 11: Make Way for Nitrux Linux 3.5.0!
Goodbye Windows 11: Make Way for Nitrux Linux 3.5.0!Goodbye Windows 11: Make Way for Nitrux Linux 3.5.0!
Goodbye Windows 11: Make Way for Nitrux Linux 3.5.0!
 
Mind map of terminologies used in context of Generative AI
Mind map of terminologies used in context of Generative AIMind map of terminologies used in context of Generative AI
Mind map of terminologies used in context of Generative AI
 
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
Cosa hanno in comune un mattoncino Lego e la backdoor XZ?
 
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
 
AI 101: An Introduction to the Basics and Impact of Artificial Intelligence
AI 101: An Introduction to the Basics and Impact of Artificial IntelligenceAI 101: An Introduction to the Basics and Impact of Artificial Intelligence
AI 101: An Introduction to the Basics and Impact of Artificial Intelligence
 
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success StoryDriving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success Story
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
 
GenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizationsGenAI Pilot Implementation in the organizations
GenAI Pilot Implementation in the organizations
 
RESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for studentsRESUME BUILDER APPLICATION Project for students
RESUME BUILDER APPLICATION Project for students
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
 
Artificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopmentArtificial Intelligence for XMLDevelopment
Artificial Intelligence for XMLDevelopment
 
Full-RAG: A modern architecture for hyper-personalization
Full-RAG: A modern architecture for hyper-personalizationFull-RAG: A modern architecture for hyper-personalization
Full-RAG: A modern architecture for hyper-personalization
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1Communications Mining Series - Zero to Hero - Session 1
Communications Mining Series - Zero to Hero - Session 1
 
Climate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing DaysClimate Impact of Software Testing at Nordic Testing Days
Climate Impact of Software Testing at Nordic Testing Days
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
 
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
 

SQLite and ORM Binding - Part 1 - Transcript.pdf

  • 1. SQLite & ORM Binding - Part I In this module we will persist data locally so we don’t have to go to the server to fetch data every time. We’ll use SQLite not because it’s the best storage medium but because it’s something I want to teach.
  • 2. SQLite Mapping ✦SQLite property objects can be mapped to SQLite tables ✦I also had to moved a couple of properties to getters/ setters as at the time I built the app the API didn’t allow excluding an entry ✦I abstracted the mapping further by creating my own persistence class © Codename One 2017 all rights reserved I’ll use the automated properties binding API to map objects to SQLite which makes working with SQLite far easier. If I didn’t have that API I might have just skipped over sqlite altogether as working with SQL is unpleasant and error prone. In order to get the mapping right I had to move some properties to getter and setters as at the time I originally did this code there was no option to exclude an entry from storage. This is no longer the case and we can now mark a property for exclusion but changing that would mean changing all the following modules and that’s nor necessarily something I want to do right now. I used a local abstraction for all the persistence code to make it more domain specific which is a good practice in general
  • 3. public class AppStorage { private static SQLMap smap; private static Database db; private static AppStorage instance = new AppStorage(); private EventDispatcher dispatcher = new EventDispatcher(); private AppStorage() {} public static AppStorage getInstance() { initStorage(); return instance; } private static void initStorage() { if(smap == null) { try { db = Display.getInstance().openOrCreate("RestDb.sql"); smap = SQLMap.create(db); AppSettings app = new AppSettings(); Dish dish = new Dish(); smap.setPrimaryKey(app, app.id); smap.setPrimaryKey(dish, dish.id); smap.setSqlType(dish.price, SQLMap.SqlType.SQL_DOUBLE); smap.createTable(app); smap.createTable(dish); } catch(IOException err) { Log.e(err); } } } AppStorage App storage abstracts the process of saving the objects, so right now it’s implemented using SQLMap but in theory we could replace that. Let’s review this class
  • 4. public class AppStorage { private static SQLMap smap; private static Database db; private static AppStorage instance = new AppStorage(); private EventDispatcher dispatcher = new EventDispatcher(); private AppStorage() {} public static AppStorage getInstance() { initStorage(); return instance; } private static void initStorage() { if(smap == null) { try { db = Display.getInstance().openOrCreate("RestDb.sql"); smap = SQLMap.create(db); AppSettings app = new AppSettings(); Dish dish = new Dish(); smap.setPrimaryKey(app, app.id); smap.setPrimaryKey(dish, dish.id); smap.setSqlType(dish.price, SQLMap.SqlType.SQL_DOUBLE); smap.createTable(app); smap.createTable(dish); } catch(IOException err) { Log.e(err); } } } AppStorage The class is implemented as a singleton and initialized on first use
  • 5. public class AppStorage { private static SQLMap smap; private static Database db; private static AppStorage instance = new AppStorage(); private EventDispatcher dispatcher = new EventDispatcher(); private AppStorage() {} public static AppStorage getInstance() { initStorage(); return instance; } private static void initStorage() { if(smap == null) { try { db = Display.getInstance().openOrCreate("RestDb.sql"); smap = SQLMap.create(db); AppSettings app = new AppSettings(); Dish dish = new Dish(); smap.setPrimaryKey(app, app.id); smap.setPrimaryKey(dish, dish.id); smap.setSqlType(dish.price, SQLMap.SqlType.SQL_DOUBLE); smap.createTable(app); smap.createTable(dish); } catch(IOException err) { Log.e(err); } } } AppStorage Initialization creates the DB instance that is kept within the application. We create the sql mapping instance to the database. Notice that the sqlite API doesn’t have separate methods for create or open and does the former implicitly which is pretty convenient.
  • 6. public class AppStorage { private static SQLMap smap; private static Database db; private static AppStorage instance = new AppStorage(); private EventDispatcher dispatcher = new EventDispatcher(); private AppStorage() {} public static AppStorage getInstance() { initStorage(); return instance; } private static void initStorage() { if(smap == null) { try { db = Display.getInstance().openOrCreate("RestDb.sql"); smap = SQLMap.create(db); AppSettings app = new AppSettings(); Dish dish = new Dish(); smap.setPrimaryKey(app, app.id); smap.setPrimaryKey(dish, dish.id); smap.setSqlType(dish.price, SQLMap.SqlType.SQL_DOUBLE); smap.createTable(app); smap.createTable(dish); } catch(IOException err) { Log.e(err); } } } AppStorage Next we create the tables if they don’t exist. We create blank object instances for the objects that we need, then define the primary key field. I also set the column type for price to be double, this is no longer necessary as newer versions of the SQLMap API detect the DoubleProperty and automatically set the right type. Create table literally issues a create statement, notice that we don’t support alter at this time although this might be introduced in the future.
  • 7. public void insert(PropertyBusinessObject d) { try { smap.insert(d); } catch(IOException err) { Log.e(err); ToastBar.showErrorMessage("Error saving: " + err); } } public void update(PropertyBusinessObject d) { try { smap.update(d); } catch(IOException err) { Log.e(err); ToastBar.showErrorMessage("Error updating storage: " + err); } } public void addDeleteListener(ActionListener<ActionEvent> onDelete) { dispatcher.addListener(onDelete); } public void removeDeleteListener(ActionListener<ActionEvent> onDelete) { dispatcher.removeListener(onDelete); } AppStorage Insert and update are pretty much direct wrappers to the builtin methods that handle the exceptions locally instead of propagating the IOException from the sqlite API
  • 8. public void insert(PropertyBusinessObject d) { try { smap.insert(d); } catch(IOException err) { Log.e(err); ToastBar.showErrorMessage("Error saving: " + err); } } public void update(PropertyBusinessObject d) { try { smap.update(d); } catch(IOException err) { Log.e(err); ToastBar.showErrorMessage("Error updating storage: " + err); } } public void addDeleteListener(ActionListener<ActionEvent> onDelete) { dispatcher.addListener(onDelete); } public void removeDeleteListener(ActionListener<ActionEvent> onDelete) { dispatcher.removeListener(onDelete); } AppStorage The reason for the delete listener is that we want the dish list to automatically remove the dish when it’s deleted by the edit form. This allows us to decouple that code and get an event from the underlying data model. We use the event dispatcher class to subscribe and fire an event here, this is a really convenient class when you are building an API
  • 9. public void delete(PropertyBusinessObject d) { try { smap.delete(d); dispatcher.fireActionEvent(new ActionEvent(d)); } catch(IOException err) { Log.e(err); ToastBar.showErrorMessage("Error deleting: " + err); } } public AppSettings fetchAppSettings() { try { AppSettings a = new AppSettings(); a.name.set(null); a.tagline.set(null); List<PropertyBusinessObject> lp = smap.select(a, null, true, 1000, 0); if(lp.size() == 0) { a = new AppSettings(); a.id.set("1"); insert(a); return a; } return (AppSettings)lp.get(0); } catch(Exception err) { Log.e(err); ToastBar.showErrorMessage("Error loading AppSettings: " + err); return null; } AppStorage Delete serves pretty much the same purpose as update and insert with the exception that it also fires the dispatch event appropriately
  • 10. public void delete(PropertyBusinessObject d) { try { smap.delete(d); dispatcher.fireActionEvent(new ActionEvent(d)); } catch(IOException err) { Log.e(err); ToastBar.showErrorMessage("Error deleting: " + err); } } public AppSettings fetchAppSettings() { try { AppSettings a = new AppSettings(); a.name.set(null); a.tagline.set(null); List<PropertyBusinessObject> lp = smap.select(a, null, true, 1000, 0); if(lp.size() == 0) { a = new AppSettings(); a.id.set("1"); insert(a); return a; } return (AppSettings)lp.get(0); } catch(Exception err) { Log.e(err); ToastBar.showErrorMessage("Error loading AppSettings: " + err); return null; } AppStorage App Settings is a special object as there is only one of it. So when we issue a select query we should never expect more than one entry. However it’s still a select query… By default when we invoke the select method if a property value isn’t null it’s added to the where clause. Both name and tagline default to non-null values and so we need to set them to null. Once that’s done we can just do the select query as usual.
  • 11. public void delete(PropertyBusinessObject d) { try { smap.delete(d); dispatcher.fireActionEvent(new ActionEvent(d)); } catch(IOException err) { Log.e(err); ToastBar.showErrorMessage("Error deleting: " + err); } } public AppSettings fetchAppSettings() { try { AppSettings a = new AppSettings(); a.name.set(null); a.tagline.set(null); List<PropertyBusinessObject> lp = smap.select(a, null, true, 1000, 0); if(lp.size() == 0) { a = new AppSettings(); a.id.set("1"); insert(a); return a; } return (AppSettings)lp.get(0); } catch(Exception err) { Log.e(err); ToastBar.showErrorMessage("Error loading AppSettings: " + err); return null; } AppStorage If there are no entries we just invoke insert for a new settings object.
  • 12. public List<PropertyBusinessObject> fetchDishes() { try { Dish d = new Dish(); List<PropertyBusinessObject> lp = smap.select(d, d.name, true, 1000, 0); // workaround for null images for(PropertyBusinessObject p : lp) { Dish dd = (Dish)p; if(dd.getFullSize() == null) { dd.setFullSize(Resources.getGlobalResources().getImage("food1.jpg")); } } return lp; } catch(Exception err) { Log.e(err); ToastBar.showErrorMessage("Error loading dishes: " + err); return null; } } AppStorage Fetching the list of dishes is similar but more standard as we return the full list of dishes. The main point of interest is the use of the name property to indicate the order by option for the select statement.
  • 13. public List<PropertyBusinessObject> fetchDishes() { try { Dish d = new Dish(); List<PropertyBusinessObject> lp = smap.select(d, d.name, true, 1000, 0); // workaround for null images for(PropertyBusinessObject p : lp) { Dish dd = (Dish)p; if(dd.getFullSize() == null) { dd.setFullSize(Resources.getGlobalResources().getImage("food1.jpg")); } } return lp; } catch(Exception err) { Log.e(err); ToastBar.showErrorMessage("Error loading dishes: " + err); return null; } } AppStorage Some dishes might not have an image within them as we create them and here we force a placeholder image to prevent such a case. I would recommend that you always use that strategy and have a good looking placeholder image. Now that we have the basic persistence object in place lets proceed to integration but first let’s discuss a conceptual issue. Why do we need persistence to begin with?
  • 14. Local & Remote DB ✦Keeping both a local and remote DB is risky but since we currently have the assumption of one user per restaurant it’s lower ✦We don’t really support working offline, this is mostly meant for faster loading and reduced server load © Codename One 2017 all rights reserved Technically we shouldn’t have a local database as all the data is in the server and local data is meaningless. In theory we could enable working offline but that’s not a current goal and would create many complexities I want to avoid. There is only one goal for this code and it’s performance. Users and developers often conflate performance with something that has to do with bits and bytes but it’s really about the decisions we make as developers. Do we go to the server or do we use local data? Do we cache? In this case it can create a conflict between the local and the remote database. We need to guard against that obviously but once data duplication exists we open up a risk. This risk is worth it for this level of performance.