Room是官方基于SQLite数据库封装的库。以前我们都是用的第三方orm,或者自己写的。
首先引入
def room_version = "2.2.3"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
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