Android 中 JetPack(三)Room数据库的使用

807 阅读7分钟

在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的使用,大体上可以分为以下几个步骤:

  1. 创建数据库实体类;
  2. 创建数据库Dao类(数据访问对象);
  3. 创建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");
        }
    };
}

这里有两个注意的点:

  1. Migrations应该用静态的,千万不要每次都创建.还有就是Migration(1, 2) 这个是哪个版本升级到哪个版本 所以这里就是从版本1升级到版本2
  2. 最顶上的version一定要跟着升级 以上两个千万不能错.

还有就是migrate方法里面可以写多个sql语句.主要是看你改了多少内容吧.

网上也有人使用 fallbackToDestructiveMigration() 去做升级,但是我觉得这个应该慎用,因为这个是删除之前的数据库重新创建,会造成以前的数据都没有了... 看场景的. 例如比较大的升级可能会用吧! 但是基本上不会用到的.所以我劝你们也慎用.


关于数据库还有很多的内容:像连表查询,sql语句等...因为我sql真的不是很好.所以就不在这里丢人现眼了.如果你sql比较好的话,看这些应该就简单不少了.我也找个时间补习一下sql.以后我在补充!!!上面这些你在看别人写Room就不会害怕了.到时候有什么不会的在补习一下就好了.