快速掌握Room数据库框架

https://www.jianshu.com/p/72c8efc3ad87

介绍
安卓开发个各位小伙伴,或多或少的都会用到数据库框架。为了帮助支持各位开发者,google推出了自己的数据库框架Room。
定义
官方介绍:The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.
翻译过来就是,Room持久型类库在SQLite的基础上提供了一个抽象层,方便大家流利的访问数据库。并且,利用了SQlite的全部强力功能。
好吧,说的很流弊。其实,也真的是很流弊。使用起来特别nice。
Demo效果(CURD还是很快的)
主要动作有:
插入一条、
插入多条、
更新记录通过主键index匹配、
查找一条记录通过主键index匹配、
查找所有的记录、
删除一条记录通过主键index匹配、
删除所有记录。

roomgif1.gif

引用
首先我们需要在项目的build.gradlle文件添加google的maven仓库
allprojects {
repositories {
jcenter()
//添加google
google()
}
}

接着在主module的build.gradle文件添加依赖
implementation ‘android.arch.persistence.room:runtime:1.0.0’
annotationProcessor ‘android.arch.persistence.room:compiler:1.0.0’
//添加测试支持,我们可以对数据库进行androidTest(后面会介绍)
implementation ‘android.arch.persistence.room:testing:1.0.0’

概念
Room设计到的概念有以下几个:
Entity:具体的bean实体,会与数据库表column进行映射
Dao:数据库访问对象,实现具体的增删改查。
RoomDatabase:提供直接访问底层数据库实现,我们应该从里面拿到具体的Dao,进行数据库操作。
了解上面的具体概念,我们就可以进行开发了,下面是具体细节处理。
配置过程
首先我们要创建具体的实体Entity
//entity声明定义,并且指定了映射数据表明
@Entity(tableName = “user”)
public class User {
//设置主键,并且定义自增增
@PrimaryKey(autoGenerate = true)
//字段映射具体的数据表字段名
@ColumnInfo(name = “uid”)
private int uid;
//字段映射具体的数据表字段名
@ColumnInfo(name = “first_name”)
private String firstName;
//字段映射具体的数据表字段名
@ColumnInfo(name = “last_name”)
private String lastName;

//必须指定一个构造方法,room框架需要。并且只能指定一个
//,如果有其他构造方法,则其他的构造方法必须添加@Ignore注解
public User() {
}
//其他构造方法要添加@Ignore注解
@Ignore
public User(int uid) {
this.uid = uid;
}
//Setter、Getter方法是需要添加的,为了支持room工作
public int getUid() {
return uid;
}

public void setUid(int uid) {
this.uid = uid;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

接着我们要创建具体的Dao
//注解配置sql语句
@Dao
public interface UserDao {
//所有的CURD根据primary key进行匹配
//------------------------query------------------------
//简单sql语句,查询user表所有的column
@Query(“SELECT * FROM user”)
List getAll();

//根据条件查询,方法参数和注解的sql语句参数一一对应
@Query(“SELECT * FROM user WHERE uid IN (:userIds)”)
List loadAllByIds(int[] userIds);

//同上
@Query("SELECT * FROM user WHERE first_name LIKE :first AND "
+ “last_name LIKE :last LIMIT 1”)
User findByName(String first, String last);

//同上
@Query(“SELECT * FROM user WHERE uid = :uid”)
User findByUid(int uid);

//-----------------------insert----------------------

// OnConflictStrategy.REPLACE表示如果已经有数据,那么就覆盖掉
//数据的判断通过主键进行匹配,也就是uid,非整个user对象
//返回Long数据表示,插入条目的主键值(uid)
@Insert(onConflict = OnConflictStrategy.REPLACE)
Long insert(User user);

//返回List数据表示被插入数据的主键uid列表
@Insert(onConflict = OnConflictStrategy.REPLACE)
List insertAll(User… users);
//返回List数据表示被插入数据的主键uid列表
@Insert(onConflict = OnConflictStrategy.REPLACE)
List insertAll(List users);

//---------------------update------------------------
//更新已有数据,根据主键(uid)匹配,而非整个user对象
//返回类型int代表更新的条目数目,而非主键uid的值。
//表示更新了多少条目
@Update()
int update(User user);
//同上
@Update()
int updateAll(User… user);
//同上
@Update()
int updateAll(List user);

//-------------------delete-------------------
//删除user数据,数据的匹配通过主键uid实现。
//返回int数据表示删除了多少条。非主键uid值。
@Delete
int delete(User user);
//同上
@Delete
int deleteAll(List users);
//同上
@Delete
int deleteAll(User… users);
}

然后自定义类继承RoomDatabase
//注解指定了database的表映射实体数据以及版本等信息
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
//RoomDatabase提供直接访问底层数据库实现,我们通过定义抽象方法返回具体Dao
//然后进行数据库增删该查的实现。
public abstract UserDao userDao();
}

最后创建对象并访问数据库
//得到AppDatabase 对象
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, “roomDemo-database”)
//下面注释表示允许主线程进行数据库操作,但是不推荐这样做。
//他可能造成主线程lock以及anr
//所以我们的操作都是在新线程完成的
// .allowMainThreadQueries()
.build();
//得到userDao对象
mUserDao = db.userDao();

//具体操作,非重点代码,不再展示细节(只展示 insertOne()),下载demo了解整个体系
 public void onClick(View view) {
switch (view.getId()) {
    case R.id.insert_one:
        insertOne();
        break;
    case R.id.insert_some:
        insertSome();
        break;
    case R.id.update_one:
        updataOne();
        break;
    case R.id.delete_one:
        deleteOne();
        break;
    case R.id.find_one:
        findOne();
        break;
    case R.id.find_all:
        findAll();
        break;
    case R.id.delete_all:
        deleteAll();
        break;
  }
}    
//非核心代码
private void insertOne() {
//防止UI线程阻塞以及ANR,所有的数据库操作,推荐开启新的线程访问。
new Thread(new Runnable() {
    @Override
    public void run() {
        //返回的是插入元素的primary key index
        Long aLong = mUserDao.insert(new User("t" + System.currentTimeMillis() / 1000, "allen"));
        if (aLong > 0) {
            String msg = "insert one success, index is " + aLong.toString();
            mBuffer.append(msg + "\n");
            Log.i(TAG, msg);
        } else {
            String msg = "insert one fail ";
            mBuffer.append(msg + "\n");
            Log.i(TAG, msg);
        }
        MainActivity.this.setMsg();
    }
}).start();

}

-------------------------------你已经掌握基本的增删改查--------------------------------
那么,我们还需要掌握数据库的更新。毕竟,我们的数据库表以及结构不是一成不变的。
更新数据库
首先,我们需要提升数据库版本号并定义Migration对象,指明数据库的变动迁移。
//指定version = 2(之前为1)
@Database(entities = {User.class}, version = 2)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();

//数据库变动添加Migration
public static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
//数据库的具体变动,我是在之前的user表中添加了新的column,名字是age。
//类型是integer,不为空,默认值是0
database.execSQL(“ALTER TABLE user "
+ " ADD COLUMN age INTEGER NOT NULL DEFAULT 0”);
}
};
}

接着,在数据库对象创建时候添加该Migration对象。
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, “roomDemo-database”)
//添加数据库变动迁移
.addMigrations(AppDatabase.MIGRATION_1_2)
//下面注释表示允许主线程进行数据库操作,但是不推荐这样做。
//他可能造成主线程lock以及anr
// .allowMainThreadQueries()
.build();

-----------------------------你已经完成了数据库的迁移-----------------------------
任何版本的数据库sql以及迁移,我们都需要记录下来。这样,我们数据库就有了一个完整的历史体系。后续也容易修改和维护。怎么记录呢?电子笔记?Out啦!
记录数据库sql
在主module的build.gradle里面添加如下代码
defaultConfig {

javaCompileOptions {
annotationProcessorOptions {
//room的数据库概要、记录
arguments = [“room.schemaLocation”:
KaTeX parse error: Expected 'EOF', got '}' at position 41: …ing()] }̲ } } s…projectDir/schemas”.toString())
}

配置完毕之后,我们在构建项目之后会在对应路径生成schemas文件夹。

TIM图片20180417220107.png

1.json和2.json分别是数据库版本1、2对应的概要、记录(json文件格式存储)

1.json文件数据如下:
{
“formatVersion”: 1,
“database”: {
“version”: 1,
//身份哈希
“identityHash”: “7fcc959eb0e5d9c2cd52cf58f7a05392”,
“entities”: [
{
“tableName”: “user”,
//建表语句
“createSql”: “CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, first_name TEXT, last_name TEXT)”,
“fields”: [
{
“fieldPath”: “uid”,
“columnName”: “uid”,
“affinity”: “INTEGER”,
“notNull”: true
},
{
“fieldPath”: “firstName”,
“columnName”: “first_name”,
“affinity”: “TEXT”,
“notNull”: false
},
{
“fieldPath”: “lastName”,
“columnName”: “last_name”,
“affinity”: “TEXT”,
“notNull”: false
}
],
“primaryKey”: {
“columnNames”: [
“uid”
],
“autoGenerate”: true
},
“indices”: [],
“foreignKeys”: []
}
],
“setupQueries”: [
“CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)”,
“INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, “7fcc959eb0e5d9c2cd52cf58f7a05392”)”
]
}
}

----------------你已经成功拿到了数据库各个版本的概要、记录数据------------------
接下来,我们应该进行数据库的测试,保证我们程序的稳健。写入如下:
@RunWith(AndroidJUnit4.class)
public class MigrationTest {
private static final String TEST_DB = “migration-test”;

@Rule
public MigrationTestHelper helper;

public MigrationTest() {
helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
AppDatabase.class.getCanonicalName(),
new FrameworkSQLiteOpenHelperFactory());
}

@Test
public void migrate1To2() throws IOException {
SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);

//你可以通过sql语句写入一些测试数据(旧版本数据库)

// db.execSQL(…);

// 关闭数据库,为了打开新的数据库

// db.close();

//打开新的数据库测试迁移等
db = helper.runMigrationsAndValidate(TEST_DB, 2, true, AppDatabase.MIGRATION_1_2);

}
}

调试migrate1To2()方法,即可测试数据库操作了。
----------------------------------完成数据库测试--------------------------------------
后续,我会抽时间加上数据表的多表查询、RxJAVA支持等。。。
总结
Room是google官方推出的一个好用的数据库框架。文章带领大家了解思路以及快速上手。
下面是Demo地址,方便大家测试学习~
地址:https://github.com/HoldMyOwn/RoomDemo.git
注:github留下了两个commit(分别对应数据库版本1和2)。大家可以通过先跑数据库版本1代码,拿到版本1数据库。然后,跑数据库版本2代码,体验数据库版本迁移。

作者:Allen___
链接:https://www.jianshu.com/p/72c8efc3ad87
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值