Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Room with Kotlin

Room with Kotlin

LINE Developer Meetup #25

Kazuki Nara

January 17, 2018
Tweet

More Decks by Kazuki Nara

Other Decks in Programming

Transcript

  1. About Me Kazuki Nara Android Developer @ AWA Co., Ltd.

    http://github.com/kazukinr http://www.facebook.com/kazuki.nara
  2. About Room - Android Architecture Components のひとつ - SQLiteDatabaseの薄いラッパー -

    Entity = テーブル定義(DDL) - Dao = SQL(クエリ・DML) - クエリの戻り型はPOJOで柔軟に定義可能 - Dao、Migrationのテストが書きやすい
  3. build.gradle apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt'

    dependencies { .... implementation 'android.arch.persistence.room:runtime:1.0.0' implementation 'android.arch.persistence.room:rxjava2:1.0.0' kapt 'android.arch.persistence.room:compiler:1.0.0' testImplementation 'android.arch.core:core-testing:1.0.0' testImplementation 'android.arch.persistence.room:testing:1.0.0' .... }
  4. Entity @Entity public class User { @PrimaryKey private long id;

    private String name; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
  5. Entity @Entity public class User { @PrimaryKey private long id;

    private String name; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ≠ @Entity data class User( @PrimaryKey var id: Long, var name: String )
  6. Entity @Entity public class User { @PrimaryKey private long id;

    private String name; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } CREATE TABLE User ( id INTEGER NOT NULL, name TEXT, PRIMARY KEY(id) ) →
  7. Entity CREATE TABLE User ( id INTEGER NOT NULL, name

    TEXT NOT NULL, PRIMARY KEY(id) ) → @Entity data class User( @PrimaryKey var id: Long, var name: String )
  8. Entity @Entity public class User { @PrimaryKey private long id;

    private String name; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } = @Entity data class User( @PrimaryKey var id: Long, var name: String? )
  9. Dao @Dao public interface UserDao { @Query("SELECT * FROM User")

    List<User> getAll(); @Query("SELECT * FROM User WHERE id = :id") User getById(String id); @Insert void insert(User user); @Update void update(User user); @Delete void delete(User user); @Query("UPDATE User SET name = :name WHERE id = :id") void updateNameById(long id, String name); }
  10. Dao @Dao interface UserDao { @Query("SELECT * FROM User") fun

    getAll(): List<User> @Query("SELECT * FROM User WHERE id = :id") fun getById(id: String): User? @Insert fun insert(user: User) @Update fun update(user: User) @Delete fun delete(user: User) @Query("UPDATE User SET name = :name WHERE id = :id") fun updateNameById(id: Long, name: String) }
  11. Dao with RxJava2 @Dao interface UserDao { @Query("SELECT * FROM

    User") fun getAll(): Flowable<List<User>> @Query("SELECT * FROM User WHERE id = :id") fun getById(id: String): Maybe<User> @Insert fun insert(user: User) @Update fun update(user: User) @Delete fun delete(user: User) @Query("UPDATE User SET name = :name WHERE id = :id") fun updateNameById(id: Long, name: String) }
  12. Migration static final MIGRATION_1_2 = Migration(1, 2) { @Override public

    void migrate(SupportSQLiteDatabase db) { db.execSQL("CREATE TABLE User2 (" + " id INTEGER PRIMARY KEY NOT NULL," + " first_name TEXT NOT NULL" + " last_name TEXT NOT NULL" + ")"); Cursor cursor = db.query("SELECT * FROM User"); while (cursor.moveToNext()) { String name = cursor.getString(cursor.getColumnIndex("name")); ContentValues values = ContentValues(); values.put("id", cursor.getString(cursor.getColumnIndex("id"))); values.put("first_name", name.split(" ")[0]); values.put("last_name", name.split(" ")[1]); db.insert("User2", OnConflictStrategy.REPLACE, values); } cursor.close(); } }
  13. Migration val migration_1_2 = object : Migration(1, 2) { override

    fun migrate(db: SupportSQLiteDatabase) { db.execSQL(""" CREATE TABLE User2 ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, last_name TEXT NOT NULL ) """.trimIndent()) db.query("SELECT * FROM User").use { cursor -> while (cursor.moveToNext()) { val name = cursor.getString(cursor.getColumnIndex("name")) val values = ContentValues().apply { put("id", cursor.getString(cursor.getColumnIndex("id"))) put("first_name", name.split(" ")[0]) put("last_name", name.split(" ")[1]) } db.insert("User2", OnConflictStrategy.REPLACE, values) } } } }
  14. Database @Database( version = 2, entities = {User.class}, exportSchema =

    false ) public abstract class MyDatabase extends RoomDatabase { public abstract UserDao userDao(); }
  15. Database @Database( version = 2, entities = arrayOf(User::class), exportSchema =

    false ) abstract class MyDatabase : RoomDatabase() { abstract fun userDao(): UserDao }
  16. Create RoomDatabase // must be Singleton. val database = Room.databaseBuilder(context,

    MyDatabase::class.java, "db-name") .addMigrations(migration_1_2) .build() // with Dagger. @Module class AppModule { @Provides fun provideApplicationContext(application: Application): Context { return application.applicationContext } @Singleton @Provides fun provideMyDatabase(context: Context): MyDatabase { return Room.databaseBuilder(context, MyDatabase::class.java, "db-name") .addMigrations(migration_1_2) .build() } }
  17. Room Tips - トランザクション管理したい - UI Threadでアクセスしたい - コード<>DBの型変換を自動化したい -

    関連Entityを同時に取得したい - 既存のSQLiteDatabaseにRoomを適用したい
  18. トランザクション管理したい @Dao abstract class UserDao { @Insert abstract fun insert(user:

    User) @Delete abstract fun delete(user: User) @Transaction fun insertAndDelete(newItem: User, oldItem: User) { insert(newItem) delete(oldItem) } }
  19. コード<>DBの型変換を自動化したい class Converters { @TypeConverter fun fromTimestamp(value: Long?): Date? {

    return value?.let { Date(it) } } @TypeConverter fun dateToTimestamp(value: Date?): Long? { return date?.let { it.time } } } @Database( version = 2, entities = arrayOf(User::class), exportSchema = false ) @TypeConverters(Converters::class) abstract class MyDatabase : RoomDatabase() { // Daos. }
  20. 関連Entityを同時に取得したい data class RoleAndUsers( var id: Long, var name: String

    ) { @Relation(parentColumn = "id", entityColumn = "roleId", entity = User::class) var users: List<User> } @Dao interface RoleDao { @Transaction @Query("SELECT * FROM Role") fun findAllWithUsers(): List<RoleAndUsers> }
  21. 既存のSQLiteDatabaseにRoomを適用したい // In case existing SQLiteDatabase schema version is 2.

    @Database( version = 3, // version must be incremented. entities = arrayOf(User::class), exportSchema = false ) abstract class MyDatabase : RoomDatabase() { // Daos. } val migration_2_3 = object : Migration(2, 3) { override fun migrate(db: SupportSQLiteDatabase) { // no-op. // Room add room_master_table into your SQLiteDatabase automatically. } } val database = Room.databaseBuilder(context, MyDatabase::class.java, "db-name") .addMigrations(migration_1_2, migration_2_3) .build()