The document discusses using a SQLite database to provide persistence for a to-do list application called TakeNotes. It describes setting up a SQLiteOpenHelper subclass called DatabaseHandler to create and manage the database. The DatabaseHandler acts as a repository that maps between objects in the business logic layer and rows in database tables, handling creating/upgrading the database and performing CRUD operations through methods like addToDo(). Queries return Cursor objects that can be transformed into lists of objects.
2. TakeNotes current state
¤ TakeNotes is able to add and delete to-do items
¤ However, when the application is closed, the list is
deleted, since items are not stored in persistent state
¤ Solution: use a database to store items
¤ Each time a to-do item is added to the list, it is stored in the
database too
¤ When the application is launched, the list is loaded from the
database
2
4. Android storage options
¤ Android provides several ways to store users and
application data:
¤ Shared Preferences
¤ Internal / External Storage
¤ Network Connections
¤ SQLite Databases
¤ Databases are particularly suited when storing large
amounts of the same structured data
4
5. Example: TakeNotes
# to-do item Text
1 Send an e-mail to T.A.’s
2 Deliver project specifications
3 Buy milk
5
¤ In TakeNotes, we need at least to store the text of each
to-do item in the list
6. Introducing SQLite
¤ SQLite is a SQL databaseengine
¤ It is implemented as a C library
¤ SQLite’s main features:
¤ Open-source
¤ Standard-compliant
¤ Lightweight
¤ Single-tier
¤ SQLite is natively supported in Android, and it provides a a
robust persistence layer over which you have total control
6
7. Setting up a database
¤ No database is automatically provided with the
application, If you want to use SQLite, you have to:
¤ Create your own database
¤ Create tables
¤ Populate it with data
¤ Any databases will be accessible by name to any class in
the application, but not outside the application
7
9. Business logic layer vs.
persistence layer
¤ You do not want the business logic code to deal directly
with the problem of persisting data
9
Activity 3
Data
source
Activity 1
Activity 2
SQL statements
are scattered
everywhere in
the codebase
If you change the
relational model
you have to
change the code
everywhere
10. Business logic layer vs.
persistence layer
¤ It would be better have a central authority responsible for
persisting the data
¤ The business logic can interact with such a central authority,
without caring of how the data are actually persisted
10
Central
authority
(repository)
Data
source
Activity 1
Activity 2
Activity 3
Businesslayer
11. The Repository pattern
¤ Since the central authority is usually referred to as the
repository, the resulting design pattern is called the
Repository pattern
¤ The repository act as a mediator between the business
logic layer and the data source layer
¤ The repository is usually implemented as a helper class
whose methods will be invoked by the business layer
code
11
12. The SQLite repository class
¤ The recommended method to create a new SQLite
database is to create a subclass of SQLiteOpenHelper
¤ SQLiteOpenHelper is the repository class that manages
interactions with the database
¤ The subclass of SQLiteOpenHelper will:
¤ Take care of opening the database if it exists
¤ Create the database if it does not exist
¤ Upgrade the database if it is necessary
12
13. The SQLite repository class
¤ DatabaseHandler is the repository class for our
application
¤ It is obtained by extending the SQLiteOpenHelper class
provided by Android
13
15. Anatomy of a SQLite database
¤ SQLite databases are stored in the
/data/data/package_name/databases folder on your
device or emulator
¤ Each SQLite database is characterized by the following
properties:
¤ A human-readable name
¤ A version number
15
16. Creating databases
¤ If not already present, the database is created on disk
the first time the application tries to access the data
¤ The database is upgraded every time a mismatch
between version numbers is detected.
¤ Typical use case:
¤ As a new app update requires a change in the database
schema, the version number is increased
¤ The app detects the mismatch and upgrades the schema of
the locally stored database
16
18. Creating databases
¤ The create/upgrade lifecycle is implemented in the
repository class by means of three methods:
¤ The constructor
¤ The onCreate() method
¤ the onUpdate() method
18
20. Overriding the constructor
¤ The repository class’ constructor informs the system that
the application wants to create a database
¤ Together with other parameters, it specifies:
¤ The name of the database
¤ The current version number
20
21. Overriding the constructor
21
Constants are
usually stored as
static data
members
The Context object is
needed to actually
create the database
(see reference)
An optional
CursorFactory,
typically just pass null
22. Overriding the onCreate()
method
¤ The onCreate() method is called when the database is
created for the first time
¤ The method is responsible for the creation of tables
¤ Remember: the method will not be called if no operation
is performed on the database
22
24. Overriding the onUpgrade()
method
¤ The onUpgrade() method upgrades the existing
database to conform to the new version
¤ This method should drop tables, add tables, or do
anything else it needs to upgrade to the new schema
version
¤ Remember: the method will not be called if no operation
is performed on the database
24
25. Overriding the onUpgrade()
method
25
Snippet taken from:
http://stackoverflow.com/a/8133640/1849221
The database is upgraded from
oldVersion to newVersion by
cascading upgrade statement
26. Overriding the onUpgrade()
method
¤ The simplest upgrading policy is to drop the old table and
create a new one
26
The todo table is
droppedThe todo table is
created from
scratch
27. Obtaining the database
¤ To access a database using the repository class, call:
¤ getReadableDatabase() to obtain a read-only instance of
the database
¤ getWritableDatabase() to obtain a read-write instance of
the database
¤ After invoking one of the two methods:
¤ If the DB does not exist, onCreate() will be called
¤ If the DB needs to be upgraded, onUpgrade() will be called
27
30. Object model vs.
Relational model
¤ The data you want to persist are usually encoded as class
instances, i.e., as objects
¤ Example: starting from TakeNotes v4, the to-do items are
modeled as instances of a ToDo class
30
31. Object model vs.
Relational model
¤ On the other hand, relation databases organize
information as tables and rows
¤ Example: the database for TakeNotes v4 can be made
of just one table named todo
31
# to-do item Text
1 Send an e-mail to T.A.’s
2 Deliver project specifications
3 Buy milk
32. Object-Relational Mapping
¤ As such:
¤ The business logic code deals with classes and objects
¤ The database deals with tables and rows
¤ We need a mediator to manage the automatic
transformation of objects to tuples and vice versa
¤ The technique of mapping objects to tuples (and vice
versa) is known as object-relational mapping (ORM)
32
33. Object-Relational Mapping
¤ A possible solution is to make the repository responsible of
providing a object-relation mapping (ORM) mechanism
33
Central
authority
(repository)
Data
source
Activity 1
Activity 2
Activity 3
Businesslayer
Java objects Tables and rows
34. Object-Relational Mapping
¤ The issue of mapping objects to tuples is a non-trivial one
¤ Developers spend effort in writing lots of code to convert row
and column data into objects
¤ Android put the entire burden on the developers
¤ It is up to the developer to correctly perform the mapping
¤ Third-party libraries are available
34
35. Writing data into the database
¤ The repository class should expose methods to persist
objects into the database
¤ Example: In TakeNotes, business logic code can persist a
ToDo class instances by invoking the addToDo method on
the repository class instance
35
36. Transforming objects into tuples
¤ The repository class needs to convert objects into tuples
¤ Android represents tuples as ContentValues class
instances
36
not needed if the id
column is
auto-incremented
37. Transforming objects into tuples
¤ Once a tuple has been populated, it is ready to be
inserted into the database
37
Object-relational
mapping
38. The NULL value hack
¤ While permitted in SQL, SQLite forbids to insert an empty
tuple, i.e., an empty ContentValues object
¤ If you pass an empty ContentValues to insert(), you
must also provide the name of a column that SQLite will
explicitly set to NULL
38
Name of the
column that
will be set to
NULL
just pass null if you
know that the
ContentValues is
not empty
39. Querying the database
¤ There exist two ways of querying the database:
¤ Use rawQuery() to execute a SQL statement directly
¤ use query() to build up a query from its component parts
39
40. Querying with rawQuery()
¤ The most immediate approach for querying the
database is to use rawQuery()
40
In case of parametric
queries, parameter
values must be
specified here
41. Querying with query()
¤ The query() method takes the discrete pieces of a
SELECT statement and builds the query from them.
¤ The pieces are:
¤ the name of the table to query against
¤ The list of columns to retrieve
¤ The WHERE clause (with possible positional parameters)
¤ The positional parameter values
¤ The GROUP BY clause, if any
¤ The HAVING clause, if any
¤ The ORDER BY clause, if any
41
42. Querying with query()
42
SELECT *
FROM todo
WHERE id=1 OR id=2
ORDER BY
clause
HAVING
clause
GROUP BY
clause
equivalent to
SELECT *
43. The result set
¤ A query result set are returned as a Cursor object
¤ A Cursor contains method for iterating over results
¤ With a Cursor you can:
¤ Find how many rows are in the result set via getCount()
¤ Iterate over the tuples via moveToFirst(), moveToNext()
and isAfterLast()
¤ Re-execute the query that created the cursor via requery()
¤ Release the cursor’s resources via close()
43
44. Result sets as lists of objects
¤ The repository class is responsible for transforming tuples in
the result set into a list of objects
¤ The repository class exposes methods that return
collection of objects
44
45. Result sets as lists of objects
45
Object-relational
mapping