在JetPack中提供了数据库的封装Room,提供了数据库的功能.由于Room很好的提供了和Rxjava和LiveData的交互能力,所以有必要学下
最近我悟出来一件事情,以前我在学习一个内容的时候,总想着更全面的去了解它,但是最近发现这个有几个问题:
- 当你连api都记不清的时候,很难全面的理解代码的含义
- 当你全面的看过了之后,你一点代码都不写的话,什么也记不住(可能是我的记性不好)
所以以后在写文章的时候,我准备先最简单的使用它,然后咱们在一点一点的往里面添加内容.这样,代码也能记住,就算记住的东西不多也能简单的使用起来.就算看别人的代码,也知道个大概流程.
本文知识点 :
- Room的介绍:
- Room的使用:
- Room的进阶使用:
1. Room的介绍
相信很多朋友在使用数据库的时候,都会被原生的数据库产生厌烦,最少我开始的时候,要是没有封装好的,那就尴尬很多了.所以就有了Room.Room在官网上是这么介绍的:
Room在SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。
其实Room最强大的还是可以结合RxJava和LiveData等多重操作符,所以更加适合现在的架构.
2. Room的使用
惯例(引入类库):
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
//Room kotlin
kapt "androidx.room:room-compiler:$room_version"
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
//Room Java
annotationProcessor "androidx.room:room-compiler:$room_version"
// optional - RxJava support for Room
implementation "androidx.room:room-rxjava2:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"
关于kotlin 和 Java 的类库大家自己区分哈,因为我两个都要用,所以就都导入了.
关于Room的使用,大体上可以分为以下几个步骤:
- 创建数据库实体类;
- 创建数据库Dao类(数据访问对象);
- 创建RoomDatabase类; 大体上的使用就是以上这几个步骤,接下来我们一点点的弄.
创建数据库的实体类
其实Room就是根据实体类生成对应的数据库中的表,所以这里就是创建实体类,添加几个注解就好了.理解一下注解的含义就好了.这么说来是不是就很简单了.
kotlin代码示例:
@Entity
data class KotlinUser(
@PrimaryKey(autoGenerate = true) val uid: Int = 0,
@ColumnInfo(name = "name") val name: String,
@ColumnInfo(name = "age") val age: String
)
java代码示例:
@Entity
public class JavaUser {
@PrimaryKey(autoGenerate = true)
public int uid;
@ColumnInfo(name = "name")
public String name;
@ColumnInfo(name = "age")
public String age;
public JavaUser( String name, String age) {
this.name = name;
this.age = age;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
小朋友你是否有很多问号? 为什么别人在看漫画,我却在看注解?
注解的含义:
-
@Entity: 实体类标记, 里面包含几个参数
- tableName 表名称(默认是类名称)
- indices 索引列表
- inheritSuperIndices 是否集成父类的索引
- primaryKeys 主键列名称列表
- foreignKeys 约束列表
- ignoredColumns 忽略类的列表
-
@PrimaryKey: 主键, 里面包含一个参数. autoGenerate(主键是否自增长) 默认是 false
-
@ColumnInfo: 列名称, 里面包含几个参数(有不懂的可以去这个注解看看里面讲解了怎么使用)
- name 列名称
- typeAffinity 列的关联类型
- index 索引字段
- collate 列的排序规则
- defaultValue 该列的默认值
-
@Ignore 忽略某个字段
简单使用的话,以上这些注解就够了.
创建数据库Dao类(数据访问对象)
数据访问对象是定义数据库交互的主要类.它们可以包括各种查询方法.说人话就是Dao类其实相当于具体实现,基本上都是写一些增删改查的代码.
我们来看下代码: kotlin代码示例:
@Dao
interface KotlinUserDao {
@Query("select * from kotlinuser")
fun getAll(): List<KotlinUser>
@Query("select * from kotlinuser where uid in (:userIds)")
fun loadAllByIds(userIds: IntArray): List<KotlinUser>
@Query("select * from kotlinuser where name like :name and age like :age limit 1")
fun findByName(name: String, age: String):KotlinUser
@Insert
fun insertAll(vararg kotlinUser: KotlinUser)
@Delete
fun delete(user: KotlinUser)
}
java代码示例:
@Dao
public interface JavaUserDao {
@Query("SELECT * FROM javauser")
List<JavaUser> getAll();
@Query("select * from javauser where uid in (:userIds)")
List<JavaUser> loadAllByIds(int[] userIds);
@Query("select * from javauser where name like :name and age like :age limit 1")
JavaUser findByName(String name, String age);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(JavaUser... users);
@Delete
void delete(JavaUser user);
}
增 -> @Insert 删 -> @Delete 改 -> @Update 查 -> @Query
上面的具体的sql语句我就不在这里讲了,要是你不会你可以找你们后台问问,后台要是个妹子你就赚了,要是个抠脚大汉,我劝你善良...
创建RoomDatabase类
这个是所有Room数据库的基类,是一个抽象类,这个类必须实现.我看了好多博客把这个类弄成单例,不过确实是方便,因为任何地方都可能用到,所以这里使用单例还是比较好控制的,操作起来也很方便...
kotlin示例代码:
@Database(entities = [KotlinUser::class], version = 1)
abstract class KotlinAppDatabase : RoomDatabase() {
companion object {
fun getInstance(context: Context): KotlinAppDatabase {
return databaseBuilder(context, KotlinAppDatabase::class.java, "kotlin_user.db").build()
}
}
abstract fun userDao(): KotlinUserDao
}
java示例代码:
@Database(entities = {JavaUser.class}, version = 1)
public abstract class JavaAppDatabase extends RoomDatabase {
public abstract JavaUserDao userDao();
private static JavaAppDatabase appDatabase;
public static JavaAppDatabase getInstance(Context context) {
if (appDatabase == null) {
synchronized (JavaAppDatabase.class) {
if (appDatabase == null) {
appDatabase = Room.databaseBuilder(context, JavaAppDatabase.class, "java_user.db").build();
}
}
}
return appDatabase;
}
}
这个类中要使用一个抽象方法去暴露相应的数据库访问对象.还有就是这个Room.databaseBuilder(context, JavaAppDatabase.class, "java_user.db").build() 这个方法需要注意一下.最后的那个参数是表名称.
当你使用的时候,直接调用相应的方法就可以了.
kotlin示例代码:
KotlinAppDatabase.getInstance(this).userDao()
.insertAll(KotlinUser(name = "笔墨Android", age = "23"))
java示例代码:
JavaAppDatabase.getInstance(JavaRoomActivity.this).userDao().insertAll(new JavaUser("笔墨Android", "22"));
这里只展示了插入的方法,至于其他方法可以直接验证.你以为这样就完了. 当你兴致勃勃运行的时候,就真的完了-_-! 因为这个方法只能在子线程运行.所以需要你把它放在子线程处理.
3. Room的进阶使用
- 数据库的更新(升级)
关于数据库的更新,关于数据库的更新.其实都是很多都是sql的语句.但是还是有必要说一下的.
一般数据库在升级的时候,基本上都是增加相应的字段.所以这里就以增加字段为例去讲解一下使用.
kotlin代码示例:
@Entity
data class KotlinUser(
@PrimaryKey(autoGenerate = true) val uid: Int = 0,
@ColumnInfo(name = "name") val name: String,
@ColumnInfo(name = "age") val age: String,
//增加的字段
@ColumnInfo(name = "sex") val sex: String
)
java代码示例:
@Entity
public class JavaUser {
@PrimaryKey(autoGenerate = true)
public int uid;
@ColumnInfo(name = "name")
public String name;
@ColumnInfo(name = "age")
public String age;
//增加了sex字段
@ColumnInfo(name = "sex")
public String sex;
public JavaUser(String name, String age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//...省略了get/set方法
}
这里增加了sex字段,这里注意java的代码,构造函数要变,但如果你要保留之前的构造函数,请在之前的构造函数添加 @Ignore 注解.因为对于Room他不知道应该选择哪个构造方法进行增删改查操作.
重点来了 ...
kotlin代码示例:
@Database(entities = [KotlinUser::class], version = 2)
abstract class KotlinAppDatabase : RoomDatabase() {
companion object {
fun getInstance(context: Context): KotlinAppDatabase {
return databaseBuilder(context, KotlinAppDatabase::class.java, "kotlin_user.db")
.addMigrations(migration1to2)
.build()
}
private val migration1to2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("alter table kotlinuser add column sex text default 0 not null")
}
}
}
abstract fun userDao(): KotlinUserDao
}
java代码示例:
@Database(entities = {JavaUser.class}, version = 2)
public abstract class JavaAppDatabase extends RoomDatabase {
public abstract JavaUserDao userDao();
private static JavaAppDatabase appDatabase;
public static JavaAppDatabase getInstance(Context context) {
if (appDatabase == null) {
synchronized (JavaAppDatabase.class) {
if (appDatabase == null) {
appDatabase = Room.databaseBuilder(context, JavaAppDatabase.class, "java_user.db")
.addMigrations(migration1to2)
.build();
}
}
}
return appDatabase;
}
public static Migration migration1to2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("alter table javauser add column sex text default 0 not null");
}
};
}
这里有两个注意的点:
- Migrations应该用静态的,千万不要每次都创建.还有就是Migration(1, 2) 这个是哪个版本升级到哪个版本 所以这里就是从版本1升级到版本2
- 最顶上的version一定要跟着升级 以上两个千万不能错.
还有就是migrate方法里面可以写多个sql语句.主要是看你改了多少内容吧.
网上也有人使用 fallbackToDestructiveMigration() 去做升级,但是我觉得这个应该慎用,因为这个是删除之前的数据库重新创建,会造成以前的数据都没有了... 看场景的. 例如比较大的升级可能会用吧! 但是基本上不会用到的.所以我劝你们也慎用.
关于数据库还有很多的内容:像连表查询,sql语句等...因为我sql真的不是很好.所以就不在这里丢人现眼了.如果你sql比较好的话,看这些应该就简单不少了.我也找个时间补习一下sql.以后我在补充!!!上面这些你在看别人写Room就不会害怕了.到时候有什么不会的在补习一下就好了.