SlideShare a Scribd company logo
1 of 14
Download to read offline
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

AngularJS - Services
AngularJS - ServicesAngularJS - Services
AngularJS - ServicesNir Kaufman
 
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 JavaScriptRyan Anklam
 
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.xTatsuya 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 applicationsJeff 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 ClassesKaniska 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.pdfShaiAlmog1
 
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 JavaScriptGuy Royse
 
Finishing the App - Part 1.pdf
Finishing the App - Part 1.pdfFinishing the App - Part 1.pdf
Finishing the App - Part 1.pdfShaiAlmog1
 
Androidaop 170105090257
Androidaop 170105090257Androidaop 170105090257
Androidaop 170105090257newegg
 
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 codeShriKant 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 developersPavel Lahoda
 
Droidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon2013 android experience lahoda
Droidcon2013 android experience lahodaDroidcon 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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 

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

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Victor Rentea
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistandanishmna97
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamUiPathCommunity
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Angeliki Cooney
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsNanddeep Nachan
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxRustici Software
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfOrbitshub
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native ApplicationsWSO2
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontologyjohnbeverley2021
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Zilliz
 

Recently uploaded (20)

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 

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.