7 Steps To Room
Room is a persistent library that is part of Android Architecture Components. Using Room makes it easier to work with SQLiteDatabase objects in your app, reduces the amount of template code, and validates SQL queries at compile time.
Here are the seven basic steps to using Room in your app:
-
Update the build.gradle dependencies under the project
allprojects { repositories { google () jcenter () } } Copy the code
Configure roomVersion in the same file (possibly versions.gradle)
Ext {... roomVersion ='... ' } Copy the code
Add Room dependencies in app/build.gradle
Dependence {... implementation"android.arch.persistence.room:runtime:$rootProject.roomVersion" annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion" androidTestimplementation "android.arch.persistence.room:testing:$rootProject.roomVersion" } Copy the code
For migrations from SQLiteDatabaseHelper to Room, you need to implement a Migration class to keep the version number of the user data required to upgrade the database. The architecture needs to be adjusted to test this migration. To do this, add the following code to app/build.gradle:
android { defaultConfig { ... // For Room migration tests javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocations": "$projectDir/schemas".toString()] } } } // For Room migration tests sourceSets { androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } } Copy the code
-
Update the Model class to Entity
- use
@Entity
Annotate the class and set the table name totableName
Properties; - Add the field corresponding to the primary key
@PrimaryKey
annotations - Setting the field
@ColumnInfo(name = "column_name")
annotations - If more than one constructor is available, add
@Ignore
Annotations tell Room which to use and which not to use.
@Entity(tableName = "Users") public class User { @PrimaryKey @ColumnInfo(name = "userid") private String mId; @ColumnInfo(name = "username") private String mUserName; @ColumnInfo(name = "last_update") private Date mDate; @Ignore public User(String userName) { mId = UUID.randomUUID().toString(); mUserName = userName; mDate = new Date(System.currentTimeMillis()); } public User(String id, String userName, Date date) { this.mId = id; this.mUserName = userName; this.mDate = date; }... }Copy the code
- use
-
Create Data Access Objects(DAOs)
DAOs is responsible for defining the methods to access the database. In the initial SQLite implementation of the project, all database queries are done using Cursor objects in the LocalUserDataSource. With Room, there is no need to associate all the cursors with code and the query operations are simply defined using annotations in the UserDao class.
For example, if we need to query all the users in the database, all we need to do is write the following code, and Room takes care of all the heavy lifting.
@Query("SELECT * FROM Users") List<User> getUsers(a); Copy the code
-
Creating a database
So far, you have defined the Users table and its associated query operations, but you have not created a database that combines the other Room components together. To do this, we need to define an abstract class that inherits From RoomDatabase. This class, decorated with the @Database annotation, lists entities contained in the Database and the DAOs that operate on them.
@Database(entities = {User.class}, version = 2) @TypeConverters(DataConverter.class) public abstract class UserDatabase extends RoomDatabase { private static UsersDatabase INSTANCE; public abstract UserDao userDao(a); Copy the code
If you are migrating from version 1 to version 2, you need to implement a Migration class that tells Room what to do when migrating. The database schema has not changed, so you only need an empty implementation.
static final Migration MIGRATION_1_2 = new Migration(1.2) { @Override public void migration(SupportSQLiteDatabase database) { // There is no change in the database}};Copy the code
Create a database object in the UsersDatabase class, define the name and migration of the database
database = Room.databaseBuilder(context.getApplicationContext(), UsersDatabse.class, "Sample.db") .addMigrations(MIGRATION_1_2) .build(); } Copy the code
For more information on how database migration is implemented and how it works underneath, see the following article:
Understanding migrations with Room
Performing database migrations with the SQLite API always made me feel like I was defusing a bomb — as if I was one…medium.com
-
In this step, you modify the LocalUserDataSource class to use the UserDao methods. To do this, first modify the constructor by removing the Context and adding the UserDao. Of course, the other classes that instantiate LocalUserDataSource also need to be modified. The UserDao method is then called to update the query database method for the LocalDataSource.
public List<User> getUsers(a) { return mUserDao.getUsers(); } Copy the code
Run the code! A nice feature of Room is that if your database operation is executed on the main thread, your app will crash and display the following exception message:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. Copy the code
-
Computer test
Now test the created UserDao, UsersDatabase, and LocalUserDataSource
Test UserDao
Create an AndroidJUnit4 test class to test the UserDao. One great feature of Room is the ability to create databases in memory, avoiding the need to clean up every test case.
@Before public void initDb(a) throws Exception { mDatabse = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getContext(), UsersDatabse.class) .build(); } Copy the code
Make sure the database connection is closed after each test
@After public void closeDb(a) throws Exception { mDatabase.close(); } Copy the code
To test inserting a User, for example, you can first insert the object and then check to see if it can be retrieved from the database.
@Test public void insertAndGetUser(a) { // When inserting a new user in the data source mDatabase.userDao().insertUser(USER); //The user can be retrieved List<User> users = mDatabase.userDao().getUsers(); assertThat(users.size(), is(1)); User dbUser = users.get(0); assertEquals(dbUser.getId(), USER.getId()); assertEquals(dbUser.getUserName(), USER.getUserName()); } Copy the code
All we need to do is create an in-memory database and get a UserDao from it as an argument to the LocalUserDataSource constructor.
@Before public void initDb(a) throws Exception { mDatabase = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getContext(), UsersDatabase.class) .build(); mDataSource = new LocalUserDataSource(mDatabase.userDao()); } Copy the code
See the blog post below to see how MigrationTsetHelper is used:
-
Delete UserDbHelper class