Android
Room

逆引きRoom的ななにか(使ったところを加筆中)


Entityクラスのなにか


宣言時の設定など

@Entity(tableName = "hoge_hoge", // Table名

Index[] indices = [], // index
foreignKeys = [ForeignKey( // 外部キー制約
entity = SuperHoge::class, // 親のEntityクラスを指定
String[] primaryKeys = [], // primaryKey指定
parentColumns = arrayOf("id"), // 親Entityの対応するカラム
childColumns = arrayOf("super_hoge_id"), // 自分の対応するカラム
onDelete = ForeignKey.CASCADE) // Deleteされたときの動作
])
class HogeHogeEntity(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id", index = true)
var id: Long = 0,
@ColumnInfo(name = "username")
var username: String?
@ColumnInfo(name = "order")
var order: Int?
)


  • index指定はカラムに直接アノテーションで指定できるので自分はそっちを使ってます。


POJO以外のData型を使うとき


  • コンバーター使う

  • RoomDatabase継承クラスのとこで以下のように指定

@TypeConverters(DateTypeConverter::class, StringListTypeConverter::class)

abstract class AppDatabase : RoomDatabase() {
abstract fun myModelDao(): MyModelDao
}


  • Entityクラスで直接カラムを指定して書ける

@Entity(tableName = "hoge")

class HogeEntity(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id", index = true)
var id: Long,
@ColumnInfo(name = "tag_names")
@TypeConverters(StringListTypeConverter::class) // Converterの指定
var tagNames: List<String>?
)


Stringの配列

class StringListTypeConverter {

@TypeConverter
fun fromStringList(strings: List<String>?): String? {
if (strings == null) {
return null
}

val result = StringWriter()
val json = JsonWriter(result)

try {
json.beginArray()

strings.forEach {
json.value(it)
}

json.endArray()
json.close()
} catch (e: IOException) {
Crashlytics.logException(e)
}

return result.toString()
}

@TypeConverter
fun toStringList(strings: String?): List<String>? {
if (strings == null) {
return null
}

val reader = StringReader(strings)
val json = JsonReader(reader)
val result = mutableListOf<String>()

try {
json.beginArray()

while (json.hasNext()) {
result.add(json.nextString())
}

json.endArray()
} catch (e: IOException) {
Crashlytics.logException(e)
}

return result
}
}


Date型

class DateTypeConverter {

@TypeConverter
fun fromTimestamp(value: Long?): Date? {
return if (value == null) null else Date(value)
}

@TypeConverter
fun toTimestamp(date: Date?): Long? {
return date?.*time*
}
}


Enum型

class HogeStatusConverter {

@TypeConverter
fun fromString(typeString: String): HogeStatus {
return BookshelfStatus.valueOf(typeString)
}

@TypeConverter
fun fromStatus(status: HogeStatus): String {
return status.toString()
}
}


  • ハマったとこ
    enum class 側で値に @SerializedName(“in_progress”) みたいに指定してるとうまくいかない。そのときは valueOf()toString() しているところを切り替える


構造体?を内包する



  • @Embededを使う

class EmbededHoge {

var id: Long
var content: String
}

@Entity(tableName = "hoge")

class HogeEntity(
~~~~~~省略~~~~~
@Embeded(prefix = "prefix_")
var eHoge : EmbededHoge
)

これで EmbededHoge 構造体が eHoge にマッピングされます。


Relationalなマッピングではなく、あくまでもHogeEntity内にEmbededHogeのカラムが prefix_idprefix_ content といった形で追加されます。


Daoクラスのなにか


宣言時の設定など


@Dao
interface HogeDao {
/* ===== SELECT ===== */
@Query("select * from hoge order by order asc")
fun getAll(): List<Hoge>

@Query("select * from hoge where username=:username order by order asc, username asc")
fun findByUserName(username: String): List<Hoge>

/* ===== INSERT ===== */
@Insert(onConflict = OnConflictStrategy.ROLLBACK) // コンフリクト時の設定
fun insert(entity: HogeEntity)

@Insert(onConflict = OnConflictStrategy.ABORT)
fun insertAll(hogeEntities: List<HogeEntity>)

/* ===== UPDATE ===== */
@Update
fun update(entity: HogeEntity)

@Query("update hoge set order=:order where id=:id")
fun updateOrder(id: Long, order: Int)

/* ===== DELETE ===== */
@Delete
fun delete(entity: HogeEntity)

@Query("delete from hoge where id in (:ids)")
fun deleteInCategoryIds(ids: List<Long>)
}


  • 基本的には見たまま


  • username=:username みたいに変数は :hogehoge と書く

  • 配列のinsertやらdeleteもいける。primaryKeyないテーブルはとうまくいかない(と思う)


ちょっと複雑なことは基本 @Query で普通にsql書く


  • ただし、LiveData などで変更通知を監視しているときは注意。

    UPDATE/DELETE/INSERT をトリガーとしているのでQueryで更新系書くと変更通知されない


  • where in もいける。List 渡して where id in (:ids) みたいに書く



UpSertしたいとき

@Insert(onConflict = OnConflictStrategy.REPLACE) をinsertメソッドに追記。

レコードが存在したら置き換え、存在しなければインサートしてくれる。


  • 注意
    以下のように外部キー制約が設定されているとHogeParent側でリプレースされたときにCASCADEでHogeEntityが消える(当たり前だけど

@Entity(tableName = "hoge",

foreignKeys = [ForeignKey(
entity = HogeParent::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("parent_id"),
onDelete = ForeignKey.CASCADE)
])
class HogeEntity {
~~~~~~~~~
}


Databaseクラスのなにか


宣言時の設定など

@Database(

entities = [ // Entityクラスをここに記載
HogeEntity::class,
FugaEntity::class
],
version = 1, // Versionを記述。変わる場合は必要に応じてmigrateを書く
exportSchema = false // schemaのExportを出力するか
)
@TypeConverters(DateTypeConverter::class, StringListTypeConverter::class) // Converter
abstract class AppDatabase : RoomDatabase() {
abstract fun hogeDao(): HogeDao // Daoを記載
abstract fun fugaDao(): FugaDao
}


exportSchema について



  • = true にしておくとapp/schemasの下にschemaのjsonを吐いてくれるらしい

  • Gradleファイルにも以下のような設定が必要

    ※どこかで見て参考にさせていただきました。リソース明記できなくてすいません。


build.gradle

defaultConfig {

~~~~~~~~~~省略~~~~~~~~~~~
javaCompileOptions {
annotationProcessorOptions {
arguments += [room.schemaLocation:$projectDir/schemas.toString()]
}
}
}


  • 他にもMigration Testのときに必要になってくるらしい(まだやってない)