Relationships 1&2: Mastery Test 3 Pg 271 Ten Steps to Improving College Reading Skills
Considering SQLite is a relational database, you tin can define relationships between entities. Even though most object-relational mapping libraries allow entity objects to reference each other, Room explicitly forbids this. To learn about the technical reasoning behind this determination, see Empathise why Room doesn't allow object references.
2 possible approaches
In Room, there are 2 means to ascertain and query a human relationship between entities: you can model the relationship using either an intermediate data class with embedded objects, or a relational query method with a multimap return type.
Intermediate data form
In the intermediate data grade approach, y'all define a information course that models the relationship between your Room entities. This data class holds the pairings between instances of 1 entity and instances of some other entity equally embedded objects. Your query methods tin can then return instances of this data grade for use in your app.
For example, you can define a UserBook
data course to represent library users with specific books checked out, and define a query method to retrieve a listing of UserBook
instances from the database:
Kotlin
@Dao interface UserBookDao { @Query( "SELECT user.name Every bit userName, book.proper name AS bookName " + "FROM user, book " + "WHERE user.id = volume.user_id" ) fun loadUserAndBookNames(): LiveData<Listing<UserBook>> } data class UserBook(val userName: String?, val bookName: String?)
Coffee
@Dao public interface UserBookDao { @Query("SELECT user.proper name As userName, volume.name AS bookName " + "FROM user, book " + "WHERE user.id = book.user_id") public LiveData<List<UserBook>> loadUserAndBookNames(); } public form UserBook { public String userName; public String bookName; }
Multimap render types
In the multimap render blazon approach, you don't need to ascertain whatever additional information classes. Instead, you lot define a multimap return type for your method based on the map structure that you lot desire and define the relationship between your entities directly in your SQL query.
For example, the post-obit query method returns a mapping of User
and Book
instances to stand for library users with specific books checked out:
Kotlin
@Query( "SELECT * FROM user" + "JOIN book ON user.id = book.user_id" ) fun loadUserAndBookNames(): Map<User, List<Book>>
Java
@Query( "SELECT * FROM user" + "JOIN book ON user.id = book.user_id" ) public Map<User, List<Book>> loadUserAndBookNames();
Choose an approach
Room supports both of the approaches described above, and you should use whichever approach works all-time for your app. This department discusses some of the reasons why you might prefer i or the other.
The intermediate data class approach allows you to avoid writing complex SQL queries, but it can as well upshot in increased code complexity due to the additional information classes that information technology requires. In brusk, the multimap return type approach requires your SQL queries to do more work; and the intermediate data grade arroyo requires your code to practise more work.
If you don't have a specific reason to use intermediate information classes, we recommend that you use the multimap render blazon approach. To acquire more than almost this approach, see Return a multimap.
The rest of this guide demonstrates how to define relationships using the intermediate data class arroyo.
Create embedded objects
Sometimes, you'd like to express an entity or data object as a cohesive whole in your database logic, even if the object contains several fields. In these situations, you lot can use the @Embedded
annotation to represent an object that you'd like to decompose into its subfields inside a table. You can then query the embedded fields just as you would for other individual columns.
For instance, your User
class can include a field of type Accost
, which represents a composition of fields named street
, city
, state
, and postCode
. To store the composed columns separately in the table, include an Address
field in the User
class that is annotated with @Embedded
, as shown in the following code snippet:
Kotlin
data class Address( val street: Cord?, val state: String?, val urban center: String?, @ColumnInfo(name = "post_code") val postCode: Int ) @Entity information grade User( @PrimaryKey val id: Int, val firstName: String?, @Embedded val address: Address? )
Coffee
public class Accost { public String street; public Cord state; public String city; @ColumnInfo(proper name = "post_code") public int postCode; } @Entity public class User { @PrimaryKey public int id; public Cord firstName; @Embedded public Address accost; }
The table representing a User
object then contains columns with the following names: id
, firstName
, street
, country
, city
, and post_code
.
If an entity has multiple embedded fields of the aforementioned blazon, you can keep each cavalcade unique by setting the prefix
holding. Room then adds the provided value to the beginning of each column proper noun in the embedded object.
Define one-to-one relationships
A one-to-one relationship between two entities is a relationship where each instance of the parent entity corresponds to exactly 1 instance of the kid entity, and vice-versa.
For example, consider a music streaming app where the user has a library of songs that they own. Each user has simply one library, and each library corresponds to exactly i user. Therefore, in that location should be a one-to-ane relationship between the User
entity and the Library
entity.
First, create a form for each of your two entities. One of the entities must include a variable that is a reference to the primary key of the other entity.
Kotlin
@Entity data class User( @PrimaryKey val userId: Long, val proper name: String, val historic period: Int ) @Entity data class Library( @PrimaryKey val libraryId: Long, val userOwnerId: Long )
Java
@Entity public class User { @PrimaryKey public long userId; public String name; public int historic period; } @Entity public class Library { @PrimaryKey public long libraryId; public long userOwnerId; }
In order to query the list of users and corresponding libraries, yous must first model the i-to-one relationship between the two entities. To do this, create a new data grade where each instance holds an instance of the parent entity and the corresponding instance of the kid entity. Add the @Relation
annotation to the instance of the child entity, with parentColumn
set to the name of the master primal column of the parent entity and entityColumn
set to the proper noun of the cavalcade of the child entity that references the parent entity's chief key.
Kotlin
data grade UserAndLibrary( @Embedded val user: User, @Relation( parentColumn = "userId", entityColumn = "userOwnerId" ) val library: Library )
Java
public class UserAndLibrary { @Embedded public User user; @Relation( parentColumn = "userId", entityColumn = "userOwnerId" ) public Library library; }
Finally, add a method to the DAO class that returns all instances of the data class that pairs the parent entity and the kid entity. This method requires Room to run two queries, so add the @Transaction
annotation to this method to ensure that the whole operation is performed atomically.
Kotlin
@Transaction @Query("SELECT * FROM User") fun getUsersAndLibraries(): List<UserAndLibrary>
Java
@Transaction @Query("SELECT * FROM User") public List<UserAndLibrary> getUsersAndLibraries();
Ascertain one-to-many relationships
A 1-to-many relationship betwixt two entities is a relationship where each instance of the parent entity corresponds to naught or more instances of the child entity, but each example of the child entity can but stand for to exactly one instance of the parent entity.
In the music streaming app example, suppose the user has the power to organize their songs into playlists. Each user can create as many playlists as they want, but each playlist is created by exactly ane user. Therefore, there should be a i-to-many relationship betwixt the User
entity and the Playlist
entity.
First, create a class for each of your 2 entities. As in the previous example, the child entity must include a variable that is a reference to the primary fundamental of the parent entity.
Kotlin
@Entity data class User( @PrimaryKey val userId: Long, val name: String, val age: Int ) @Entity data class Playlist( @PrimaryKey val playlistId: Long, val userCreatorId: Long, val playlistName: String )
Java
@Entity public course User { @PrimaryKey public long userId; public String proper noun; public int age; } @Entity public class Playlist { @PrimaryKey public long playlistId; public long userCreatorId; public Cord playlistName; }
In society to query the list of users and corresponding playlists, you lot must get-go model the one-to-many relationship between the two entities. To do this, create a new data form where each instance holds an example of the parent entity and a list of all corresponding child entity instances. Add the @Relation
annotation to the case of the child entity, with parentColumn
set to the name of the primary key column of the parent entity and entityColumn
set to the name of the column of the child entity that references the parent entity's primary fundamental.
Kotlin
data class UserWithPlaylists( @Embedded val user: User, @Relation( parentColumn = "userId", entityColumn = "userCreatorId" ) val playlists: List<Playlist> )
Coffee
public grade UserWithPlaylists { @Embedded public User user; @Relation( parentColumn = "userId", entityColumn = "userCreatorId" ) public List<Playlist> playlists; }
Finally, add a method to the DAO class that returns all instances of the data grade that pairs the parent entity and the child entity. This method requires Room to run two queries, so add together the @Transaction
annotation to this method to ensure that the whole operation is performed atomically.
Kotlin
@Transaction @Query("SELECT * FROM User") fun getUsersWithPlaylists(): List<UserWithPlaylists>
Java
@Transaction @Query("SELECT * FROM User") public List<UserWithPlaylists> getUsersWithPlaylists();
Define many-to-many relationships
A many-to-many human relationship between two entities is a relationship where each instance of the parent entity corresponds to zero or more instances of the kid entity, and vice-versa.
In the music streaming app instance, consider once again the user-defined playlists. Each playlist can include many songs, and each song tin be a function of many different playlists. Therefore, there should be a many-to-many relationship between the Playlist
entity and the Song
entity.
First, create a grade for each of your 2 entities. Many-to-many relationships are distinct from other relationship types because at that place is generally no reference to the parent entity in the kid entity. Instead, create a 3rd class to correspond an associative entity (or cross-reference table) between the two entities. The cross-reference table must take columns for the master fundamental from each entity in the many-to-many relationship represented in the table. In this example, each row in the cross-reference table corresponds to a pairing of a Playlist
instance and a Song
case where the referenced song is included in the referenced playlist.
Kotlin
@Entity data class Playlist( @PrimaryKey val playlistId: Long, val playlistName: Cord ) @Entity data class Song( @PrimaryKey val songId: Long, val songName: String, val creative person: Cord ) @Entity(primaryKeys = ["playlistId", "songId"]) information class PlaylistSongCrossRef( val playlistId: Long, val songId: Long )
Java
@Entity public class Playlist { @PrimaryKey public long playlistId; public String playlistName; } @Entity public form Vocal { @PrimaryKey public long songId; public String songName; public String artist; } @Entity(primaryKeys = {"playlistId", "songId"}) public class PlaylistSongCrossRef { public long playlistId; public long songId; }
The next step depends on how you want to query these related entities.
- If you want to query playlists and a list of the corresponding songs for each playlist, create a new data class that contains a single
Playlist
object and a list of all of theSong
objects that the playlist includes. - If you want to query songs and a listing of the respective playlists for each, create a new information class that contains a unmarried
Song
object and a listing of all of thePlaylist
objects in which the vocal is included.
In either case, model the human relationship between the entities by using the associateBy
property in the @Relation
annotation in each of these classes to identify the cross-reference entity providing the relationship between the Playlist
entity and the Vocal
entity.
Kotlin
information class PlaylistWithSongs( @Embedded val playlist: Playlist, @Relation( parentColumn = "playlistId", entityColumn = "songId", associateBy = Junction(PlaylistSongCrossRef::class) ) val songs: Listing<Song> ) data class SongWithPlaylists( @Embedded val vocal: Song, @Relation( parentColumn = "songId", entityColumn = "playlistId", associateBy = Junction(PlaylistSongCrossRef::class) ) val playlists: List<Playlist> )
Java
public grade PlaylistWithSongs { @Embedded public Playlist playlist; @Relation( parentColumn = "playlistId", entityColumn = "songId", associateBy = @Junction(PlaylistSongCrossref.class) ) public Listing<Vocal> songs; } public course SongWithPlaylists { @Embedded public Song song; @Relation( parentColumn = "songId", entityColumn = "playlistId", associateBy = @Junction(PlaylistSongCrossref.class) ) public List<Playlist> playlists; }
Finally, add together a method to the DAO class to expose the query functionality your app needs.
-
getPlaylistsWithSongs
: This method queries the database and returns all of the resultingPlaylistWithSongs
objects. -
getSongsWithPlaylists
: This method queries the database and returns all of the resultingSongWithPlaylists
objects.
These methods each crave Room to run 2 queries, and then add the @Transaction
annotation to both methods to ensure that the whole operation is performed atomically.
Kotlin
@Transaction @Query("SELECT * FROM Playlist") fun getPlaylistsWithSongs(): List<PlaylistWithSongs> @Transaction @Query("SELECT * FROM Song") fun getSongsWithPlaylists(): List<SongWithPlaylists>
Java
@Transaction @Query("SELECT * FROM Playlist") public Listing<PlaylistWithSongs> getPlaylistsWithSongs(); @Transaction @Query("SELECT * FROM Song") public List<SongWithPlaylists> getSongsWithPlaylists();
Define nested relationships
Sometimes, you might need to query a set of three or more tables that are all related to each other. In that instance, you would ascertain nested relationships between the tables.
Suppose that in the music streaming app example, you desire to query all of the users, all of the playlists for each user, and all of the songs in each playlist for each user. Users have a one-to-many relationship with playlists, and playlists have a many-to-many human relationship with songs. The post-obit lawmaking example shows the classes that represent these entities, also as the cross-reference table for the many-to-many relationship between playlists and songs:
Kotlin
@Entity data class User( @PrimaryKey val userId: Long, val name: Cord, val age: Int ) @Entity information grade Playlist( @PrimaryKey val playlistId: Long, val userCreatorId: Long, val playlistName: String ) @Entity data class Song( @PrimaryKey val songId: Long, val songName: String, val artist: Cord ) @Entity(primaryKeys = ["playlistId", "songId"]) data form PlaylistSongCrossRef( val playlistId: Long, val songId: Long )
Java
@Entity public course User { @PrimaryKey public long userId; public String name; public int age; } @Entity public grade Playlist { @PrimaryKey public long playlistId; public long userCreatorId; public String playlistName; } @Entity public class Vocal { @PrimaryKey public long songId; public Cord songName; public Cord artist; } @Entity(primaryKeys = {"playlistId", "songId"}) public course PlaylistSongCrossRef { public long playlistId; public long songId; }
Showtime, model the relationship betwixt two of the tables in your fix every bit you normally would, with a data class and the @Relation
annotation. The following example shows a PlaylistWithSongs
class that models a many-to-many human relationship between the Playlist
entity class and the Vocal
entity class:
Kotlin
data class PlaylistWithSongs( @Embedded val playlist: Playlist, @Relation( parentColumn = "playlistId", entityColumn = "songId", associateBy = Junction(PlaylistSongCrossRef::class) ) val songs: Listing<Song> )
Java
public class PlaylistWithSongs { @Embedded public Playlist playlist; @Relation( parentColumn = "playlistId", entityColumn = "songId", associateBy = Junction(PlaylistSongCrossRef.class) ) public Listing<Song> songs; }
Afterward y'all define a information class that represents this relationship, create another data class that models the relationship between another tabular array from your set and the first human relationship class, "nesting" the existing relationship within the new one. The post-obit instance shows a UserWithPlaylistsAndSongs
form that models a 1-to-many human relationship between the User
entity form and the PlaylistWithSongs
relationship class:
Kotlin
data class UserWithPlaylistsAndSongs( @Embedded val user: User @Relation( entity = Playlist::form, parentColumn = "userId", entityColumn = "userCreatorId" ) val playlists: Listing<PlaylistWithSongs> )
Coffee
public class UserWithPlaylistsAndSongs { @Embedded public User user; @Relation( entity = Playlist.class, parentColumn = "userId", entityColumn = "userCreatorId" ) public List<PlaylistWithSongs> playlists; }
The UserWithPlaylistsAndSongs
class indirectly models the relationships between all iii of the entity classes: User
, Playlist
, and Song
. This is illustrated in effigy i.
If at that place are any more tables in your set, create a form to model the relationship between each remaining table and the relationship class that models the relationships betwixt all previous tables. This creates a chain of nested relationships between all of the tables that you want to query.
Finally, add a method to the DAO class to expose the query functionality that your app needs. This method requires Room to run multiple queries, and then add the @Transaction
notation to ensure that the whole operation is performed atomically:
Kotlin
@Transaction @Query("SELECT * FROM User") fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>
Java
@Transaction @Query("SELECT * FROM User") public List<UserWithPlaylistsAndSongs> getUsersWithPlaylistsAndSongs();
Additional Resources
To learn more than almost defining relationships between entities in Room, see the post-obit additional resources.
Samples
- Android Sunflower
- Tivi
Videos
- What's New in Room (Android Dev Tiptop '19)
Blogs
- Database relations with Room
Source: https://developer.android.com/training/data-storage/room/relationships
0 Response to "Relationships 1&2: Mastery Test 3 Pg 271 Ten Steps to Improving College Reading Skills"
Post a Comment