AndroidX---Room

使用Room保存数据到本地数据库。Room是官方基于SQLite数据库封装的库。

Room有3个组件:

1、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:

    1、Be an abstract class that extends RoomDatabase.
    2、Include the list of entities associated with the database within the annotation.
    3、Contain an abstract method that has 0 arguments and returns the class that is annotated with @Dao.
At runtime, you can acquire an instance of Database by calling Room.databaseBuilder() or Room.inMemoryDatabaseBuilder().

2、Entity: Represents a table within the database.

3、DAO: Contains the methods used for accessing the database.

1、DataBase:单例,创建数据库管理类,包含Dao实例

2、Dao:提供数据库操作方法

3、Entity:描述一个表的属性字段

一、DataBase

@Database(entities = arrayOf(User::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

// 创建数据库
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "database-name").build()

1.1、Use type converters:类型转换

有些我们常用的类型不能在数据库中保存,这时候我们需要将其转换成数据库能保存的类型。

以下的Date在Database中会被Converters转换成Long类型数据再保存到数据库

class Converters {
    @TypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return value?.let { Date(it) }
    }

    @TypeConverter
    fun dateToTimestamp(date: Date?): Long? {
        return date?.time?.toLong()
    }
}

@Database(entities = arrayOf(User::class), version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

@Entity
data class User(private var birthday: Date?)

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")
    fun findUsersBornBetweenDates(from: Date, to: Date): List<User>
}

二、Dao

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

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

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

    @Insert(onConflict = REPLACE)
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)
}

@Insert(onConflict = REPLACE):新增数据时如果有冲突就替换旧的。

三、Entity

@Entity
data class User(
    @PrimaryKey var id: Int,
    var firstName: String?,
    var lastName: String?
)

@Entity:表示注册一个table,表名为类名

@PrimaryKey:每个@Entity都必须有一个@PrimaryKey

这里的字段默认是public的,如果设置为private,则需要提供setter和getter

3.1、使用PrimaryKey

@Entity(primaryKeys = arrayOf("firstName", "lastName"))
data class User(
    @PrimaryKey var id,
    var firstName: String?,
    var lastName: String?
)

每个表必须定义PrimaryKey,如果只有一个可以使用@PrimaryKey定义,如果有多个,则需要使用primaryKeys=arrayOf()定义,primaryKeys是@Entity的属性,当然@Entity还有其他属性,接下来会慢慢说明。

3.2、定义表名

@Entity(tableName = "users")
data class User (
    // ...
)

表名默认为类名,如果需要表名跟类名不同,可以使用@Entity的属性tableName来设置

3.3、定义表字段

@Entity(tableName = "users")
data class User (
    @PrimaryKey var id: Int,
    @ColumnInfo(name = "first_name") var firstName: String?,
    @ColumnInfo(name = "last_name") var lastName: String?
)

@ColumnInfo:可选,重定义Table的字段名,默认使用属性名作为字段名,如果需要字段名跟属性名不同,可以如此设置。

3.4、Ignore fields:忽略属性

@Entity
data class User(
    @PrimaryKey var id: Int,
    var firstName: String?,
    var lastName: String?,
    @Ignore var picture: Bitmap?
)

open class User {
    var picture: Bitmap? = null
}

@Entity(ignoredColumns = arrayOf("picture"))
data class RemoteUser(
    @PrimaryKey var id: Int,
    var hasVpn: Boolean
) : User()

@Ignore:有时候我们不需要将类的所有属性都保存到数据库,这时候就需要添加@Ignore来忽略这些属性了。

3.5、Index specific columns:索引

@Entity(indices = arrayOf(Index(value = ["last_name", "address"])))
data class User(
    @PrimaryKey var id: Int,
    var firstName: String?,
    var address: String?,
    @ColumnInfo(name = "last_name") var lastName: String?,
    @Ignore var picture: Bitmap?
)

@Entity(indices = arrayOf(Index(value = ["first_name", "last_name"],
        unique = true)))
data class User(
    @PrimaryKey var id: Int,
    @ColumnInfo(name = "first_name") var firstName: String?,
    @ColumnInfo(name = "last_name") var lastName: String?,
    @Ignore var picture: Bitmap?
)

indices:可以创建索引以加快查询速度。

unique:唯一性,有时候除了primaryKey之外其他字段的值也可能是需要唯一的,免得存在重复数据,比如身份证号等。

3.6、Define relationships between objects:外键

@Entity(foreignKeys = arrayOf(ForeignKey(
            entity = User::class,
            parentColumns = arrayOf("id"),
            childColumns = arrayOf("user_id"),
            onDelete = CASCADE, onUpdate = CASCADE)
       )
)
data class Book(
    @PrimaryKey var bookId: Int,
    var title: String?,
    @ColumnInfo(name = "user_id") var userId: Int
)

foreignKeys:Room是禁止Entity包含Entity的。只能使用foreignKeys设置。

CASCADE:表示关联删除/更新

3.7、Create nested objects:定义非Entity属性

data class Address(
    var street: String?,
    var state: String?,
    var city: String?,
    @ColumnInfo(name = "post_code") var postCode: Int
)

@Entity
data class User(
    @PrimaryKey var id: Int,
    var firstName: String?,
    @Embedded var address: Address?
)

@Embedded:可添加非Entity类属性,Entity表中不会出现非Entity类字段,而是将非Entity类的属性作为Entity表的字段。

参考以上代码,User表的字段为:id, firstName, street, state, city, post_code

四、DatabaseView

@DatabaseView("SELECT user.id, user.name, user.departmentId," +
        "department.name AS departmentName FROM user " +
        "INNER JOIN department ON user.departmentId = department.id")
data class UserDetail(
    var id: Long,
    var name: String?,
    var departmentId: Long,
    var departmentName: String?
)

@Database(entities = arrayOf(User::class),
          views = arrayOf(UserDetail::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

注册DatabaseView

展开阅读全文