L9 - Data Storage 2
L9 - Data Storage 2
OVERVIEW
SAVE DATA (STORAGE)
• Few options to save your
mobile app data:
• App-specific storage
• Shared storage
• Preferences
• Database Part 2
• Overview
• SQLite
• Data Entities
• Data Access Objects
• Object Relationship
OVERVIEW
DATABASE – RECALL FROM PART1
• Structured data stored in a private database.
• After the Jetpack library – Room Persistence Library is
introduced to handle all database access.
• Room is a persistence library that's part of Android
Jetpack.
• Room is an abstraction layer on top of a SQLite database.
• Do not need explicit permission to access the data.
• Other applications CANNOT access the data.
• The data will be removed when the mobile app is
uninstalled.
LOCAL DATABASE
• It’s always important to store data locally – that the user
can access it (and still able to perform some crucial
operations) while the application is offline.
• SQLite is automatically created when the mobile
application is installed on the mobile device.
SETUP ROOM
• Add dependencies in build.gradle file:
Add dependencies
PRIMARY COMPONENTS
• There are THREE major
components involved in
Room:
• Database class
• Hold the database and
provide access to the
persisted data.
• Data Access Objects (DAO)
• Provide methods to
perform operations in the
database.
• Data Entities
• Represents table in the
database.
Room Library Architecture
PRIMARY COMPONENTS
• The database class provides your app with instances of the
DAOs associated with that database.
• In turn, the app can use the DAOs to retrieve data from the
database as instances of the associated data entity objects.
• The app can also use the defined data entities to update rows
from the corresponding tables, or to create new rows for
insertion.
DATA ENTITIES
• Use Room to define the database schema without
writing any SQL code.
• Use data entities to represent the object to store.
• Each entity = table in the associated Room database.
• Each Room entity is a class annotated as @entity
• Each instance = row of data in the table.
DATA ENTITIES
• To define the data entity:
@entity annotation
@Entity
public class User {
@PrimaryKey Table name
public int id;
Field names (and data type)
public String firstName;
public String lastName;
} Primary Key using @PrimaryKey
annotation (MUST define one)
DATA ENTITIES
• To define the data entity (customization):
@ColumnInfo(name = "first_name")
public String firstName; if the field names and
column names are different
@ColumnInfo(name = "last_name")
public String lastName;
}
DATA ENTITIES
• To define the data entity (customization):
DATA ENTITIES
• To define the data entity (customization):
DATA ENTITIES
• To define the data entity (ignore field):
@Entity
public class User {
@PrimaryKey
public int id;
DATA ENTITIES
• To define the data entity (ignore field):
@Dao
public interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertUsers(User... users);
@Insert
public void insertBothUsers(User user1, User user2);
@Insert
public void insertUsersAndFriends(User user, List<User> friends);
}
@Dao
public interface UserDao {
@Update To update one or more users
public void updateUsers(User... users);
}
@Dao
public interface UserDao {
@Delete To delete one or more users
public void deleteUsers(User... users);
}
// You can also define this class in a separate file, as long as you add the
// "public" access modifier.
static class UserBook {
Reference for LiveData:
public String userName;
public String bookName; https://developer.android.com/topic/
} libraries/architecture/livedata
}
OBJECT RELATIONSHIP
• SQLite is a structured and relational database.
• A relationship can be defined between entities:
• Embedded object
• One-to-One relationship
• One-to-Many relationship
• Many-to-Many relationship
• And others.
OBJECT RELATIONSHIP
EMBEDDED OBJECT
• Group relevant (and cohesive) fields as a whole object.
• Use @Embedded annotation to represent the object that
is decomposed into its subfield within the table.
public class Address {
public String street;
public String state;
public String city;
OBJECT RELATIONSHIP
EMBEDDED OBJECT
• Group relevant (and cohesive) fields as a whole object.
• Use @Embedded annotation to represent the object that
is decomposed into its subfield within the table.
public class Address {
public String street;
public String state;
public String city;
OBJECT RELATIONSHIP
1-TO-1 RELATIONSHIP
• Each instance of the parent entity corresponds to
exactly one instance of the child entity.
• For example:
• A music streaming app where a user has a library of
songs.
• One user can only have one library, while a library can
only correspond to one user.
OBJECT RELATIONSHIP
1-TO-1 RELATIONSHIP
• Example (music streaming app)
@Entity
public class Library { One of the entities must
@PrimaryKey public long libraryId; include one variable that
public long userOwnerId; references to the primary
} key of another entity.
OBJECT RELATIONSHIP
1-TO-1 RELATIONSHIP
• Before query, model the 1-to-1 relationship between
these two entities:
public class UserAndLibrary { Create a new data class
@Embedded public User user; that has two instances
@Relation( (one from parent entity
parentColumn = "userId", and one from child entity)
entityColumn = "userOwnerId"
) Use @Relation annotation
public Library library; to the instance of the child
} entity, and set:
parentColumn – primary
key in parent entity
entityColumn – column
name in child entity that
references to
[email protected]
parentColumn
41
OBJECT RELATIONSHIP
1-TO-1 RELATIONSHIP
• Lastly, to perform the query (add Query method to
DAO class):
Returns all instances that
pairs and parent-child
@Transaction entities.
@Query("SELECT * FROM User")
public List<UserAndLibrary> getUsersAndLibraries();
Use @Transaction
annotation because two
queries will be performed
- To ensure the queries are
performed atomically
OBJECT RELATIONSHIP
1-TO-MANY RELATIONSHIP
• Each instance in the parent entity corresponds to zero
to many instances of the child entity
• BUT each child can only correspond to exactly one
instance in the parent entity.
• For example (music streaming app)
• One user can create zero to many playlists, but each
playlist belongs to only one user.
• Create two classes – User and Playlist
@Entity @Entity
public class User { public class Playlist {
@PrimaryKey public long userId; @PrimaryKey public long playlistId;
public String name; public long userCreatorId;
public int age; public String playlistName;
} }
OBJECT RELATIONSHIP
1-TO-MANY RELATIONSHIP
• Before query, model the 1-to-many relationship
between these two entities:
public class UserWithPlaylists { Codes are similar with 1-to-
@Embedded public User user; 1 relationship.
@Relation(
parentColumn = "userId",
entityColumn = "userCreatorId"
)
public List<Playlist> playlists; The only difference is the
} list of child instances.
• To query:
@Transaction
@Query("SELECT * FROM User")
public List<UserWithPlaylists> getUsersWithPlaylists();
OBJECT RELATIONSHIP
MANY-TO-MANY RELATIONSHIP
• Each instance in the parent entity corresponds to zero
to many instances of the child entity
• AND each child instance corresponds to zero-to-many
instances in the parent entity.
• For example (music streaming app)
• Each playlist can include many songs.
• Each song can be in many playlists.
OBJECT RELATIONSHIP
MANY-TO-MANY RELATIONSHIP
• Many-to-many relationship: @Entity
public class Playlist {
@PrimaryKey public long playlistId;
No reference to the parent public String playlistName;
entity in the child entity. }
OBJECT RELATIONSHIP
MANY-TO-MANY RELATIONSHIP
• Next step?
• If you want to query many playlists with a list of songs for
each playlist:
• Create a new data class that contains a
single Playlist object and a list of all Song objects that the
playlist includes.
• If you want to query many songs with a list of playlists for
each song:
• Create a new data class that contains a single Song object
and a list of all playlist objects that the song includes.
OBJECT RELATIONSHIP
MANY-TO-MANY RELATIONSHIP
public class PlaylistWithSongs {
@Embedded public Playlist playlist;
@Relation(
parentColumn = "playlistId",
entityColumn = "songId",
associateBy = @Junction(PlaylistSongCrossref.class)
)
public List<Song> songs;
} Use associateBy property
in @Relation to model the
public class SongWithPlaylists { relationship.
@Embedded public Song song;
@Relation(
parentColumn = "songId",
entityColumn = "playlistId",
associateBy = @Junction(PlaylistSongCrossref.class)
)
public List<Playlist> playlists;
}
[email protected]
48
OBJECT RELATIONSHIP
MANY-TO-MANY RELATIONSHIP
• To query, on DAO class:
Returns all the resulting
@Transaction PlaylistWithSongs objects.
@Query("SELECT * FROM Playlist")
public List<PlaylistWithSongs> getPlaylistsWithSongs();
@Transaction
@Query("SELECT * FROM Song")
public List<SongWithPlaylists> getSongsWithPlaylists();
MORE REFERENCES
• Asynchronous Query Execution (running database
query on the main thread):
• https://developer.android.com/training/data-
storage/room/async-queries
• Test and debug database
• https://developer.android.com/training/data-
storage/room/testing-db