Posted by on 12th December 2018

Save data in a local database using Room

Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.

There are 3 major components in Room:

  • Database: Contains the database holder and serves as the main access point for the underlying connection to your app’s persisted, relational data.The class that’s annotated with @Database should satisfy the following conditions:
  • Entity: Represents a table within the database.
  • DAO: Contains the methods used for accessing the database.

The following code snippet contains a sample database configuration with one entity and one DAO:\

@Entity
public class User {
    @PrimaryKey
    public int uid;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;
}

UserDao

@Dao
public interface UserDao {
    @Query("SELECT * FROM user")
    List<User> getAll();

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    List<User> loadAllByIds(int[] userIds);

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    User findByName(String first, String last);

    @Insert
    void insertAll(User... users);

    @Delete
    void delete(User user);
}

AppDatabase

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

After creating the files above, you get an instance of the created database using the following code:

AppDatabase db = Room.databaseBuilder(getApplicationContext(),
        AppDatabase.class, "database-name").build();

Note: If your app runs in a single process, you should follow the singleton design pattern when instantiating an AppDatabase object. Each RoomDatabase instance is fairly expensive, and you rarely need access to multiple instances within a single process.

If your app runs in multiple processes, include enableMultiInstanceInvalidation() in your database builder invocation. That way, when you have an instance of AppDatabase in each process, you can invalidate the shared database file in one process, and this invalidation automatically propagates to the instances of AppDatabase within other processes.

Database Queries

Insert

When you create a DAO method and annotate it with @Insert, Room generates an implementation that inserts all parameters into the database in a single transaction.

@Dao
public interface MyDao {

    @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);

}

Update

The Update convenience method modifies a set of entities, given as parameters, in the database. It uses a query that matches against the primary key of each entity.

@Dao
public interface MyDao {

    @Update
    public void updateUsers(User... users);

}

Delete

The Delete convenience method removes a set of entities, given as parameters, from the database. It uses the primary keys to find the entities to delete.

@Dao
public interface MyDao {

    @Delete
    public void deleteUsers(User... users);

}

Query for information

@Query is the main annotation used in DAO classes. It allows you to perform read/write operations on a database. Each @Query method is verified at compile time, so if there is a problem with the query, a compilation error occurs instead of a runtime failure.

Room also verifies the return value of the query such that if the name of the field in the returned object doesn’t match the corresponding column names in the query response, Room alerts you in one of the following two ways:

  • It gives a warning if only some field names match.
  • It gives an error if no field names match.
@Dao
public interface MyDao {

    @Query("SELECT * FROM user")

    public User[] loadAllUsers();

}

Passing parameters into the query

@Dao
public interface MyDao {

    @Query("SELECT * FROM user WHERE age > :minAge")

    public User[] loadAllUsersOlderThan(int minAge);

}

You can also pass multiple parameters or reference them multiple times in a query, as shown in the following code snippet:

@Dao
public interface MyDao {

    @Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")

    public User[] loadAllUsersBetweenAges(int minAge, int maxAge);

    @Query("SELECT * FROM user WHERE first_name LIKE :search " +
           "OR last_name LIKE :search")

    public List<User> findUserWithName(String search);

}

Returning subsets of columns

public class NameTuple {

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;

}

@Dao
public interface MyDao {

    @Query("SELECT first_name, last_name FROM user")
    public List<NameTuple> loadFullName();

}

Passing a collection of arguments

Some of your queries might require you to pass in a variable number of parameters, with the exact number of parameters not known until runtime. For example, you might want to retrieve information about all users from a subset of regions. Room understands when a parameter represents a collection and automatically expands it at runtime based on the number of parameters provided.

@Dao
public interface MyDao {

    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")

    public List<NameTuple> loadUsersFromRegions(List<String> regions);

}

Observable queries

When performing queries, you’ll often want your app’s UI to update automatically when the data changes. To achieve this, use a return value of type LiveData in your query method description. Room generates all necessary code to update the LiveData when the database is updated.

@Dao
public interface MyDao {

    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")

    public LiveData<List<User>> loadUsersFromRegionsSync(List<String> regions);

}

Reactive queries with RxJava

Room provides the following support for return values of RxJava2 types:

To use this functionality, include the latest version of the rxjava2 artifact in your app’s build.gradle file:

app/build.gradle
dependencies {

    implementation 'androidx.room:room-rxjava2:2.1.0-alpha02'

}
@Dao
public interface MyDao {

    @Query("SELECT * from user where id = :id LIMIT 1")

    public Flowable<User> loadUserById(int id);

    // Emits the number of users added to the database.
    @Insert
    public Maybe<Integer> insertLargeNumberOfUsers(List<User> users);

    // Makes sure that the operation finishes successfully.
    @Insert
    public Completable insertLargeNumberOfUsers(User... users);

    /* Emits the number of users removed from the database. Always emits at
       least one user. */
    @Delete
    public Single<Integer> deleteUsers(List<User> users);

}

Direct cursor access

If your app’s logic requires direct access to the return rows, you can return a Cursor object from your queries, as shown in the following code snippet:

@Dao
public interface MyDao {

    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")

    public Cursor loadRawUsersOlderThan(int minAge);

}

Querying multiple tables

@Dao
public interface MyDao {

    @Query("SELECT * FROM book " +
           "INNER JOIN loan ON loan.book_id = book.id " +
           "INNER JOIN user ON user.id = loan.user_id " +
           "WHERE user.name LIKE :userName")

   public List<Book> findBooksBorrowedByNameSync(String userName);

}

You can also return POJOs from these queries. For example, you can write a query that loads a user and their pet’s name as follows:

@Dao
public interface MyDao {

   @Query("SELECT user.name AS userName, pet.name AS petName " +
          "FROM user, pet " +
          "WHERE user.id = pet.user_id")

   public LiveData<List<UserPet>> loadUserAndPetNames();

   // You can also define this class in a separate file, as long as you add the

   // "public" access modifier.
   static class UserPet {
       public String userName;
       public String petName;
   }

}

Reference:

Please follow and like us:

Comments

Be the first to comment.

Leave a Reply


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*


Enjoy this blog? Please spread the word :)