12/08/2018, 16:30

7 Steps To Room

Room là 1 thư viện nằm trong Android Architecture Components. Nó giúp các lập trình viên thao thao tác dễ dàng hơn với SQLiteDatabase trong ứng dụng của mình. Giảm thiểu số lượng mà lỗi, thừa và kiểm tra truy vấn lúc biên dịch chương trình. Sau đây là 7 bước cơ bản để bạn có thể áp dụng ROOM vào ...

Room là 1 thư viện nằm trong Android Architecture Components. Nó giúp các lập trình viên thao thao tác dễ dàng hơn với SQLiteDatabase trong ứng dụng của mình. Giảm thiểu số lượng mà lỗi, thừa và kiểm tra truy vấn lúc biên dịch chương trình.

Sau đây là 7 bước cơ bản để bạn có thể áp dụng ROOM vào ứng dụng của mình.

Cập nhật lại Build Gradle, khởi tạo các entities, DAOs và database, thay thế SQLiteDatabase bằng cách gọi các phương thức trong DAOs , kiểm tra mọi thứ bạn khỏi tạo và xóa những class không cần thiết.

Trong demo này mình sẽ xây ứng dụng hiển thị và chỉnh sửa tên người dùng, lưu dữ liệu xuống database như kiểu là 1 User object.

  • sqlite: Sử dụng SQLiteOpenHelper và các giao thức SQLite truyền thống.
  • room: Thay thế bằng Room và cung các giao thức truy vấn. Áp dụng mơ hình Model-View-Presenter và làm việc với class UserRepository.

Room’s dependencies được cung cấp qua Maven repository của Google’s. Đơn giản là bạn chỉ cần thêm 1 vài khai báo vào trong build.gradle chính của dự án.

allprojects {
    repositories {
        google()
        jcenter()
    }
}

ext {
   ... 
    roomVersion = '1.0.0-alpha4'
}

trong app/build.gradle

android {
    defaultConfig {
        ...
       // used by Room, to test migrations
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = ["room.schemaLocation": 
                                 "$projectDir/schemas".toString()]
            }
        }
    }

    // used by Room, to test migrations
    sourceSets {
        androidTest.assets.srcDirs += 
                           files("$projectDir/schemas".toString())
    }

dependencies{
 …
implementation  "android.arch.persistence.room:runtime:$rootProject.roomVersion"
annotationProcessor    "android.arch.persistence.room:compiler:$rootProject.roomVersion"
androidTestImplementation    "android.arch.persistence.room:testing:$rootProject.roomVersion"
}

Room khỏi tạo các bảng thông qua annotated @Entity. Các trường trong class là các cột trong bảng.

  • Annotate @Entity sử dụng tableName để định nghĩa tên của bảng.
  • Sử dụng @PrimaryKey để định nghĩa khóa chính của bảng.
  • Sử dụng @ColumnInfo(name = “column_name”) để định nghĩa tên các cột trong bảng.
  • Nếu có nhiều constructors thì các bạn thêm @Ignore để khai báo cho Room biết là có sử dụng constructors này hay không.
@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;
    }
...
}

DAOs có trách nhiệm định nghĩa các phương trức giao tiếp với cơ sở dữ liệu. Trong mô hình sử dụng SQLite truyền thống công việc này được thực hiện trong LocalUserDataSource thông qua lớp Cursor. Với Room chúng ta không cần sử dụng Cursor và đơn giản là định nghĩa các truy vấn sử dụng annotations trong UserDao.class.

@Query(“SELECT * FROM Users”)
List<User> getUsers();

Bây giờ chúng ta đã định nghĩa bảng Users nhưng chúng ta chưa khởi tạo database để chưa các bảng lại với nhau. Để làm việc này chúng ta sẽ định nghĩa 1 lớp trừu tượng (abstract class) kế thừa từ RoomDatabase. Class này được định nghĩa với annotated @Database chưa các danh sách thực thế trong database và các DAOs liên quan đến chúng. Với mỗi lần thay đổi class này thì chúng ta lại tăng version lên 1 như trong vd này tôi sẽ tăng vesion của database lên thành 2.

@Database(entities = {User.class}, version = 2)
@TypeConverters(DateConverter.class)
public abstract class UsersDatabase extends RoomDatabase {

    private static UsersDatabase INSTANCE;

    public abstract UserDao userDao();
    ....
 }

Bởi vì khi tăng version database lên thêm 1 nhưng vẫn muốn giữa lại database cũ nên chúng ta cần khai báo 1 lớp Migration để nói với Room những gì ta muốn giữ lại khi nâng từ version 1->2.

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
// Since we didn't alter the table, there's nothing else to do here.
    }
};

Trong class UsersDatabase này chúng ta sẽ định nghĩa tên và database và migration chúng.

database = Room.databaseBuilder(context.getApplicationContext(),
        UsersDatabase.class, "Sample.db")
        .addMigrations(MIGRATION_1_2)
        .build();

các bạn có thể tìm hiểu rõ hơn về migration trong Room tại đây.

Chúng ta đã khởi tạo Database, bảng User, và liên kết, truy vấn đến database và bây giờ là lúc chúng ta sủe dụng chúng.

Đầu tiên là chúng ta sẽ cập nhật file LocalUserDataSource để sử dụng UserDao. Việc đầu tiên là chung ta cần loại bỏ Context và thêm UserDao.

Bước tiếp theo chúng ta sẽ viết hàm truy vấn từ database bằng cách gọi truy vấn từ UserDao.

public List<User> getUsers() {
   return mUserDao.getUsers();
}

Đến đây khi lần đầu chạy ứng dụng bạn sẽ bị Exception là java.lang.IllegalStateException khi bạn khỏi tạo, truy vấn dữ liệu trên main thread. Bạn cần sử dụng các truy vấn ở 1 thread khác bằng cách đơn giản nhất là sử dụng Runnable.

Trong bài sau mình sẽ giới thiệu với các bạn các kết hợp Room với RxJava để truy vấn dữ liệu.

Testing UserDao

Để test UserDao chúng ta cần khỏi tạo AndroidJUnit4 test class. Khả năng tuyệt vời của Room là chúng có thể tạo 1 lớp dữ liệu ngay trên bộ nhớ Ram. Điều này giúp chung ta không cần xóa dữ liệu test trong cơ sở dữ liệu sau khi chạy các test case.

@Before
public void initDb() throws Exception {
    mDatabase = Room.inMemoryDatabaseBuilder(
                           InstrumentationRegistry.getContext(),
                           UsersDatabase.class)
                    .build();
}
....
@After
public void closeDb() throws Exception {
    mDatabase.close();
}

Test khỏi tạo 1 User mới trong cơ sở dữ liệu và kiểm tra xem User đã được thêm mới thành công hay chưa.

@Test
public void insertAndGetUser() {
    // 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());
}

Testing the UserDao usage in LocalUserDataSource

@Before
public void initDb() throws Exception {
    mDatabase = Room.inMemoryDatabaseBuilder(
                           InstrumentationRegistry.getContext(),
                           UsersDatabase.class)
                    .build();
    mDataSource = new LocalUserDataSource(mDatabase.userDao());
}

Và cuối cùng hãy chắn ràng bãn đã đóng cơ sở dũ liệu sau khi truy vấn.

Loại bỏ tất cả các class không sử dụng và các chức năng được thay thế bằng Room. Trong class chúng ta sẽ không sẻu dụng UsersDbHelper.class và SQLiteOpenHelper.class. Việc sử dụng Room kiếm việc kiểm soát mã lỗi dễ dàng hơn, các truy vấn được kiểm tra tại thời điểm biên dịch làm cho mọi mã lỗi đều có thể kiểm xoát được.

Một chương trình test thử về Room bạn có thể tham khảo tại đây..

Bài viết được dịch từ: https://medium.com/google-developers/7-steps-to-room-27a5fe5f99b2.

0