Jetpack Room数据库

一、基础使用

1.1 Room三个主要操作类

  • Entity 实体类 代表一个表中的字段
  • Dao数据库操作接口提供增删改查
  • 管理创建数据库

1.2 引入依赖

https://developer.android.com/jetpack/androidx/releases/room
选择性引入

dependencies {
  def room_version = "2.2.5"

  implementation "androidx.room:room-runtime:$room_version"
  annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor

  // optional - Kotlin Extensions and Coroutines support for Room
  // implementation "androidx.room:room-ktx:$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"

  // Test helpers
  testImplementation "androidx.room:room-testing:$room_version"
}

1.3 创建数据库实体 Entity

@Entity(tableName = "User")  //数据库中表名
public class User {
    //主键 自增
    @PrimaryKey(autoGenerate = true)
    private int id;
    @ColumnInfo(name = "user_name", typeAffinity = ColumnInfo.TEXT) //实际数据库中的字段user_name 数据类型
    private String name;
    @ColumnInfo(name = "user_gender")
    private String gender;
    private int age;
    @Ignore //忽略字段
    private int flag;

    public User(int id, String name, String gender, int age) {
        this.id = id;
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    public User(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    @Ignore
    public User(String gender, int age) {
        this.gender = gender;
        this.age = age;
    }
  // 省略 get set ......
}

构造方法中的形参代表查询时需要查询的字段,如果有多个构造方法 可以使用注解@Ignore忽略, 同样

1.4 创建数据库操作接口 Dao

@Dao //Database access object  数据库访问接口 所有增删改查等操作都在此声明
public interface UserDao {

    // long Not sure how to handle insert method's return type.long
    @Insert
    void insertUser(User... users);

    // int 影响的行数
    @Update
    int updateUser(User... users);

    @Delete
    void deleteUser(User... users);

    @Query("DELETE FROM USER")
    void deleteUser();

    @Query("SELECT * FROM USER ORDER BY ID DESC")
    List<User> getAllUser();
}

1.4.1 唯一约束+REPLACE实现有则更新无则插入

  • 实体类中通过indices 将name字段设置成唯一约束
@Entity(tableName = "chatrow", indices = {@Index(value = {"name"}, unique = true)} )  //数据库实体类
public class User {...}
  • Dao中插入语句设置onConflict插入模式
public interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertUser(User... users);
}

1.5 创建数据库管理 Database

// entities ={多个表逗号隔开}
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {
    public abstract UserDao getUserDao();
    // 若entities有多个实例,则应该写多个Dao
}

1.6 使用

操作数据库必须在子线程中,为了简单此处直接使用allowMainThreadQueries()强制在主线程运行,正常开发不允许

MyDatabase myDatabase = Room.databaseBuilder(this, MyDatabase.class, "user")
        .allowMainThreadQueries()//强制在主线程运行 不建议
        .build();
UserDao userDao = myDatabase.getUserDao();
//增加
userDao.insertUser(new User("nikola", "男", 22));
//修改ID为3的
int row = userDao.updateUser(new User(3, "kolia", "女", 22));
//查询所有
List<User> allUser = userDao.getAllUser();

二、使用单例注意事项

Google官方称创建数据库实例较耗时,所以使用单例模式,但要注意: 无参构造方法不能使用private修饰 否则Room自动生成实现类时会报错

// entities ={多个集合逗号隔开}
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {

    private static final String DATABASE_NAME="my_db.db";
    private static MyDatabase myDatabase;

    public MyDatabase() {
    }
    
    synchronized public static MyDatabase getInstance(Context context) {
        if (null == myDatabase) {
            myDatabase = Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, DATABASE_NAME)
                    .allowMainThreadQueries() //强制在主线程运行 不建议
                    .build();
        }
        return myDatabase;
    }

    public abstract UserDao getUserDao();
    // 若entities有多个实例,则应该写多个Dao
}

指定数据库db文件保存路径

synchronized public static MyDatabase getInstance(Context context) {
        if (null == myDatabase) {
            File priExternalDir = FileUtil.getPriExternalDir(context, Environment.DIRECTORY_DOWNLOADS);
            final String dbPath = new File( priExternalDir.getAbsolutePath()+File.separator+DATABASE_NAME).getPath();
            myDatabase = Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, dbPath)
                    .allowMainThreadQueries() //强制在主线程运行 不建议
                    .build();
        }
        return myDatabase;
    }

三、 LiveData 监听数据库变化

3.1 返回值使用LiveData包装

数据变化只在查询时提现,所以修改UserDao中查询所有的返回值类型, 且LiveData内部已经实现再子线程中执行,可以直接调用

@Query("SELECT * FROM USER ORDER BY ID DESC")
    // List<User> getAllUser();
    LiveData<List<User>> getAllUserLive();

3.2 设置监听

LiveData<List<User>> listLiveData  = userDao.getAllUserLive();
listLiveData.observe(this, new Observer<List<User>>() {
    @Override
    public void onChanged(List<User> users) {
     ......
    }
});

当对数据库进行增删改时会回调onChanged

四、数据库版本迁移

  • 变更Entity中的字段 要增加 Database中的version,
  • 迁移数据库有保留原有数据与 破坏式清空原有数据

4.1 破坏式

allowMainThreadQueries 不保留数据直接删除原有数据库

MyDatabase myDatabase = Room.databaseBuilder(this, MyDatabase.class, "user")
    .allowMainThreadQueries()//强制在主线程运行 不建议
    .fallbackToDestructiveMigration() //破坏式: 当数据库版本变化时不做数据迁移
    .build();

4.2 保留原有数据迁移

当Entity字段改变时调用.addMigrations(Migration... migrations),指定从某个版本迁移到某个版本需要做的某项操作,具体操作定义在Migration中

MyDatabase myDatabase = Room.databaseBuilder(this, MyDatabase.class, "user")
    .allowMainThreadQueries()//强制在主线程运行 不建议
    .addMigrations(migration) //保留原有数据
    .build();
  • 表中增加type字段时的addMigrations 实参 migration如下,需要自行写增加字段语句
static final Migration migration = new Migration(2,3) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE user ADD COLUMN type INTEGER NOT NULL DEFAULT 1");
    }
};
  • 删除某个字段,sqlLet没有删除字段语句,只能创建新的数据库定义需要的字段,将原有数据库数据复制过去,删除旧数据库后再将新数据库重命名
    new Migration(3,4) 表示从版本3升级到版本4
static final Migration migration = new Migration(3,4) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("CREATE TABLE user_temp (id INTEGER PRIMARY KEY NOT NULL , user_name TEXT," +
                "user_gender TEXT)");
        database.execSQL("INSERT INTO user_temp (id, user_name, user_gender) " +
                "SELECT id, user_name, user_gender FROM user");
        database.execSQL("DROP TABLE user");
        database.execSQL("ALTER TABLE user_temp RENAME to user");
    }
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335