Room库

一 引言

 

Room是Jetpack组件中一个对象关系映射(ORM)库。可以很容易将 SQLite 表数据转换为 Java 对象。

  • Room 在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。
  • 支持与LiveData、RxJava、Kotlin协成组合使用。
  • Google 官方强烈推荐使用Room。

二 优点

  1. 编译期检查,Room会在编译的时候验证每个@Query@Entity等,它不仅检查语法问题,还会检查是否有该表,这就意味着几乎没有任何运行时错误的风险
  2. 较少的模板代码
  3. 与 LiveData集成

Room有3个主要组件:

  • Entity : 数据实体,对应数据库中的表
  • DAO:数据访问对象,包含访问数据库的方法
  • Database:数据库持有者。

三 Room的架构图

在这里插入图片描述

四 基础使用

1 引入配置

 implementation 'android.arch.persistence.room:runtime:1.0.0'
    annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'

2 使用

在使用数据的时候,需要主要涉及到Room三个部分:

  • Entity: 数据库中表对应的实体
  • Dao: 操作数据库的方法
  • DataBase: 创建数据库实例

1.Entity(创建表) 

创建表一般会用到下面几个注解:

@Entity(tableName=“表名称”)

定义一个表

@PrimaryKey(autoGenerate=true)

定义主键

@ColumnInfo(name = “列名”)

定义列名

@Ignore

忽略某个字段

@Entity(tableName = UserModel.USER_TABLE_NAME,
        indices = {@Index(value = {UserModel.FACE_ID}, unique = true),
                @Index(value = {UserModel.NAME}, unique = true)})
public class UserModel implements Parcelable {

    public static final String USER_TABLE_NAME = "user" ;


    public static final String NAME = "name";
    public static final String FACE_ID = "faceId";

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = BaseColumns._ID)
    public long id;

    @NonNull
    @ColumnInfo(name = FACE_ID)
    public String faceId;

    @NonNull
    @ColumnInfo(name = NAME)
    public String name;

    public UserModel(@NonNull String faceId, @NonNull String name) {
        this.faceId = faceId;
        this.name = name;
    }
   
}

2.Dao

@Insert@Delete@Update@Query 代表我们常用的插入、删除、更新、查询数据库操作。

  • DAO是数据访问对象,指定SQL查询,并让他与方法调用相关联。
  • DAO必须是一个接口或者抽象类。
  • 默认情况下,所有的查询都必须在单独的线程中执行
    @Dao
    public interface UserDao {
    
        @Query("SELECT * FROM "+ UserModel.USER_TABLE_NAME + " WHERE "+
                UserModel.NAME + " = :name")
        LiveData<UserModel> queryByName2Lv(String name);
    
    
        @Query("SELECT * FROM "+ UserModel.USER_TABLE_NAME + " WHERE "+
                UserModel.NAME + " = :name")
        UserModel queryByName2Model(String name);
    
    
        @Query("SELECT * FROM "+ UserModel.USER_TABLE_NAME + " WHERE "+
                UserModel.FACE_ID + " = :faceId")
        LiveData<UserModel> queryByFaceId2Lv(String faceId);

        @Query("SELECT * FROM "+ UserModel.USER_TABLE_NAME + " WHERE "+
                UserModel.FACE_ID + " = :faceId")
        UserModel queryByFaceId2Model(String faceId);
    
        @Query("SELECT COUNT(*) FROM " + UserModel.USER_TABLE_NAME)
        int count();
    
    
        @Query("SELECT * FROM " + UserModel.USER_TABLE_NAME)
        LiveData<List<UserModel>> queryAllByLv();
    
    
        @Query("SELECT * FROM " + UserModel.USER_TABLE_NAME)
        List<UserModel> queryAll();
    
        @Update
        public int updateUsers(List<UserModel> userModels);
    
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        long insertUser(UserModel userModel);
    
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        long[] insertAllUser(List<UserModel> userModels);

        @Delete
        void delete(UserModel... userModels);

        @Delete
        void deleteAll(List<UserModel> userModels);

        @Query("DELETE FROM " + UserModel.USER_TABLE_NAME + " WHERE " +
                UserModel.FACE_ID + " = :faceId")
        int deleteByFaceId(String faceId);

    }

3.DataBase

  • 创建一个抽象类继承自RoomDatabase

  • 给他添加一个注解@Database表名它是一个数据库,注解有两个参数第一个是数据库的实体,它是一个数组,可以传多个,当数据库创建的时候,会默认给创建好对应的表,第二个参数是数据库的版本号

  • 定义跟数据库一起使用的相关的DAO类

  • 创建一个RoomDemoDatabase的单例,防止同时打开多个数据库的实例

  • 使用Room提供的数据库构建器来创建该实例,第一个参数application,第二个参数当前数据库的实体类,第三个参数数据库的名字

  • exportSchema = true 支持导出Room生成的配置文件

创建好这三个组件,Room的配置基本就已经完成了,通常我们需要的业务逻辑都加写在Dao中的抽象方法中。  

4.数据库迁移

在Room中,数据库迁移使用的是Migration对象,定义如下:

添加新的实体类

@Entity(tableName = FaceModel.FACE_TABLE_NAME,
        foreignKeys = {
                @ForeignKey(entity = UserModel.class,
                        parentColumns = "faceId",
                        childColumns = "faceId",
                        onUpdate = ForeignKey.CASCADE,
                        onDelete = ForeignKey.CASCADE
                )
        },
        indices = {@Index(value = {"faceId"})}
)
public class FaceModel  {

    public static final String FACE_TABLE_NAME = "face";


    public static final String TYPE = "type";
    public static final String FACE_ID = "faceId";
    public static final String PATH = "path";


    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = BaseColumns._ID)
    public long id;

    @NonNull
    @ColumnInfo(name = PATH)
    public String path;

    @ColumnInfo(name = TYPE)
    public int type;

    @NonNull
    @ColumnInfo(name = FACE_ID)
    public String faceId;

    public FaceModel(@NonNull String path, int type, @NonNull String faceId) {
        this.path = path;
        this.type = type;
        this.faceId = faceId;
    }

}

 配置实体类

@Database(entities =  {
    UserModel.class, FaceModel.class
        },
    version = 2, exportSchema = true)

1 startVersion是旧版本号,endVersion是新版本号。数据库版本发生变更(如升级)会回调migrate函数,我们需要在此回调中编写版本变更的相关代码,例如创建表、添加列等等。

  public abstract class Migration {
    public final int startVersion;
    public final int endVersion;

    public Migration(int startVersion, int endVersion) {
        this.startVersion = startVersion;
        this.endVersion = endVersion;
    }

    public abstract void migrate(@NonNull SupportSQLiteDatabase database);
}

把Migration添加到对应的db中:

 val appDb = Room.databaseBuilder(this, AppDatabase::class.java, "myDb.db")
            .allowMainThreadQueries()
            .addMigrations(object : Migration(1,2){
                override fun migrate(database: SupportSQLiteDatabase) {
                  //升级相关操作
                }
            })
            .build()

 2 Sql 升级语句,可以根据Room导出的json文件获取

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        Log.i(TAG, "migrate: ");
        // Create the new table
          String sql = "CREATE TABLE IF NOT EXISTS face (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `path` TEXT NOT NULL, `type` INTEGER NOT NULL, `faceId` TEXT NOT NULL, FOREIGN KEY(`faceId`) REFERENCES `user`(`faceId`) ON UPDATE CASCADE ON DELETE CASCADE )";
        database.execSQL(
                sql);
        String sql2 = "CREATE INDEX IF NOT EXISTS `index_face_faceId` ON face (`faceId`)";
        database.execSQL(
                sql2);
    }
};

private static RoomDemoDatabase buildDatabase(final Context appContext) {
    return Room.databaseBuilder(appContext, RoomDemoDatabase.class, DATABASE_NAME)
            .allowMainThreadQueries()
             .addMigrations(MIGRATION_1_2)
             //.openHelperFactory(new SafeHelperFactory("123456".toCharArray()))
            .addCallback(new Callback() {
                @Override
                public void onCreate(@NonNull SupportSQLiteDatabase db) {
                    super.onCreate(db);

                }

                @Override
                public void onOpen(@NonNull SupportSQLiteDatabase db) {
                    super.onOpen(db);
                }

            })
            .build();
}

 多版本升级

.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_1_4)

 五 Room 关联表

关联表配置

  • foreignKeys 配置外键
  • parentColumns:父表外键
  • childColumns:子表外键
  • NO_ACTION: parent表中某行被删掉(更新)后。child表中与parent这一行发生映射的行不发生任何改变
  • RESTRICT: parent表中想要删除(更新)某行。如果child表中有与这一行发生映射的行。那么改操作拒绝。
  • SET_NULL/SET_DEFAULT:parent表中某行被删掉(更新)后。child表中与parent这一行发生映射的行设置为NULL(DEFAULT)值。
  • CASCADE:parent表中某行被删掉(更新)后。child表中与parent这一行发生映射的行被删掉(其属性更新到对应设置)
    @Entity(tableName = FaceModel.FACE_TABLE_NAME,
        foreignKeys = {
                @ForeignKey(entity = UserModel.class,
                        parentColumns = "faceId",
                        childColumns = "faceId",
                        onUpdate = ForeignKey.CASCADE,
                        onDelete = ForeignKey.CASCADE
                )
        },
        indices = {@Index(value = {"faceId"})}
    )

创建嵌套对象

    public class UserAndFaceModel {
    
        @Relation(parentColumn = "faceId", entityColumn = "faceId", entity = FaceModel.class)
        public List<FaceModel> faceModels;
    
        @Embedded
        public UserModel userModel;
    
    }

 创建关联Dao

@Dao
public interface UserAndFaceDao {

    @Transaction // 保障事务
    @Query("SELECT * FROM "+ UserModel.USER_TABLE_NAME + " WHERE "+
            UserModel.NAME + " = :name")
    LiveData<UserAndFaceModel> queryByName2Lv(String name);

    @Transaction
    @Query("SELECT * FROM "+ UserModel.USER_TABLE_NAME + " WHERE "+
            UserModel.NAME + " = :name")
    UserAndFaceModel queryByName2Model(String name);

    @Transaction
    @Query("SELECT * FROM "+ UserModel.USER_TABLE_NAME + " WHERE "+
            UserModel.FACE_ID + " = :faceId")
    LiveData<UserAndFaceModel> queryByFaceId2Lv(String faceId);

    @Transaction
    @Query("SELECT * FROM "+ UserModel.USER_TABLE_NAME + " WHERE "+
            UserModel.FACE_ID + " = :faceId")
    UserAndFaceModel queryByFaceId2Model(String faceId);

    @Transaction
    @Query("SELECT * FROM "+ UserModel.USER_TABLE_NAME )
    List<UserAndFaceModel> queryAll();

}

关联表数据插入注意

保障事务

RoomDemoDatabase.getInstance(MainActivity.this.getApplicationContext()).runInTransaction(new Runnable() {
            @Override
            public void run() {
                UserModel userModel = new UserModel(1, "1", "2");
                RoomDemoDatabase.getInstance(MainActivity.this.getApplicationContext()).userDao().insertUser(userModel);
                FaceModel faceModel = new FaceModel("fa", 1, "fa");
                RoomDemoDatabase.getInstance(MainActivity.this.getApplicationContext()).faceDao().insertFace(faceModel);
            }
        });

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值