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