为什么要用ROOM
对于Android 开发人员来说,其实对数据库操作是很不熟练的,要写一堆Sql,很是麻烦,于是出现了很多开源的库方便我们操作,为此google 官方提出了Room 的库,解决sqlite 封装问题,毕竟是google 的,后期肯定会持续维护的.
ROOM 使用架构
如何使用
1,添加gradle
def room_version = "2.5.1"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// optional - RxJava2 support for Room
implementation "androidx.room:room-rxjava2:$room_version"
// optional - RxJava3 support for Room
implementation "androidx.room:room-rxjava3:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"
// optional - Test helpers
testImplementation "androidx.room:room-testing:$room_version"
// optional - Paging 3 Integration
implementation "androidx.room:room-paging:$room_version"
2,添加表格
@Entity(tableName = "book")
public class Book {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id",typeAffinity = ColumnInfo.INTEGER)
public int id;
@ColumnInfo(name = "bookName",typeAffinity = ColumnInfo.TEXT)
public String bookName;
public Book(int id, String bookName) {
this.id = id;
this.bookName = bookName;
}
@Ignore
public Book(String bookName) {
this.bookName = bookName;
}
}
对应的是上面的Entity,一个Entity 对应一个表格(本例例中是book),一个表格对应一个Dao
@Entity(tableName = "author") 改写自己的表名
@PrimaryKey(autoGenerate = true) 主键是否自增长
@ColumnInfo(name = "bookName",typeAffinity = ColumnInfo.TEXT) 分别对应字段名和字段类型
2,生成Dao
有表格了,肯定要有表格的一系列操作增删查改,这就是Dao 的意义
@Dao
public interface BookDao {
@Insert
void insertBook(Book book);
@Delete
void deleteBook(Book book);
@Update
void updateBook(Book book,Auther author);
@Query("SELECT * FROM book")
List<Book> queryBook();
@Query("SELECT * FROM book WHERE bookName = :name")
List<Book> queryBookByname(String name);
}
加上注解@Dao 即可,其他的没什么区别
3,创建数据库对象
@Database(entities = {Book.class,Auther.class},version = 1)
public abstract class MyRoomDataBase extends RoomDatabase {
private static final String DB_name = "roomdb";
private static MyRoomDataBase myRoomDataBase;
public static synchronized MyRoomDataBase getInstance(Context context){
if (myRoomDataBase == null){
myRoomDataBase = Room.databaseBuilder(context.getApplicationContext(),MyRoomDataBase.class,DB_name).build();
}
return myRoomDataBase;
}
public abstract BookDao bookDao();
public abstract AuthorDao authorDao();
}
@Database(entities = {Book.class,Auther.class},version = 1) 数据库中对应表的Entity 类,我这边创建了两种表 Book,Author,
version = 1 对应的是数据版本,升级的时候可以修改
DB_name 修改自己的数据库名称
bookDao 实例化表操作实例
4,app 调用
MyRoomDataBase myRoomDataBase = MyRoomDataBase.getInstance(this);
List<Auther> authers = new ArrayList<>();
authers.add(new Auther("张三",1));
authers.add(new Auther("李四",1));
new Thread(()->{
myRoomDataBase.runInTransaction(new Runnable() {
@Override
public void run() {
myRoomDataBase.authorDao().insertAuthor(authers);
myRoomDataBase.bookDao().insertBook(new Book("两个人同时写的书"));
}
});
}).start();
new Thread(()->{
List<Book> books = myRoomDataBase.bookDao().queryBookByname("两个人同时写的书");
for (Book book:books){
Log.e("xss",book.bookName);
}
}).start();
我这边因为实例话两张表,所以有两个表操作.这里面需要注意的是调用myRoomDataBase.runInTransaction() 保证同一事务完成操作,即要么都成功,要么都失败.
OK 基本用法就是这样啦
升级数据库
一般是因为表字段有变动
//修改版本号为2
@Database(entities = {Book.class,Auther.class},version = 2)
public abstract class MyRoomDataBase extends RoomDatabase {
//.....
public static synchronized MyRoomDataBase getInstance(Context context){
if (myRoomDataBase == null){
myRoomDataBase = Room.databaseBuilder(context.getApplicationContext(),MyRoomDataBase.class,DB_name).addMigrations(M_1_2).build();
}
return myRoomDataBase;
}
//升级后操作
static final Migration M_1_2 = new Migration(1,2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
//一般不需要做任何事情
}
};
}
升级新增M_1_2,表明版本1 升级到2,如果新增到3,可继续添加
//升级后操作
static final Migration M_2_3 = new Migration(2,3) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
}
};
Room.databaseBuilder(context.getApplicationContext(),MyRoomDataBase.class,DB_name).addMigrations(M_1_2,M_2_3).build();
直接修改表
@Entity(tableName = "book")
public class Book {
//.......省略多余的
@ColumnInfo(name = "time",typeAffinity = ColumnInfo.TEXT)
public String time;
public Book(int id, String bookName,String time) {
this.id = id;
this.bookName = bookName;
this.time = time;
}
//.....
}
一般都是1升级到2,2升级到3,
如果是1直接升级到3,也需要实现对应的Migration,否则会报错.Migration 空实现就可以了,一般不需要做任何事.
重新运行,用命令行进入应用数据库位置,用下面命令查看,是否生效.
adb shell;
cd /data/data/com.example.testroom/databases;
sqlite3 roomdb;//进入数据库
不知道table 名字的可以
.tables;//参考所有表
sqlite> pragma table_info ('book');//查看表结构
0|id|INTEGER|1||1
1|bookName|TEXT|0||0
2|time|TEXT|0||0
这样其实也挺麻烦的,ROOM 也提供了可视化的查看方式Schema
在app 的build.gradle 中添加
android {
//......
defaultConfig {
//.....
javaCompileOptions {
annotationProcessorOptions {
arguments += [
"room.schemaLocation":"$projectDir/schemas".toString(),
"room.incremental":"true"
]
}
}
}
$projectDir/schemas
这段的意思是在跟目录下schemas 下记录表格升级的具体详情,每次升级都会记录.
可以看下,我的目录生成了两次升级的信息文件,具体内容我就不加附件了哈,可自行调试.
ps:@Database(entities = {Book.class,Auther.class},exportSchema = true ,version = 2)
exportSchema = true 可以配置是否导出schema 文件,默认是导出的,建议配置导出哈
初始化数据库里面的内容
一些固定数据默认就可以初始化,查看ROOM 迭代过程有新增
Room.databaseBuilder(context.getApplicationContext(),MyRoomDataBase.class,DB_name).createFromAsset("xxxx").addMigrations(M_1_2).build();
Room.databaseBuilder(context.getApplicationContext(),MyRoomDataBase.class,DB_name).
createFromFile(new File("xxxx"))
.createFromAsset("xxxx")
负责加载本地数据,来初始化当前的.
结合ViewModel 使用Room
数据库查询完成还要通知上层更新UI ,这是以前的操作,现在不需要了,可以自动监听数据改变,不要你通知啦
后续更新....
参考
https://developer.android.com/training/data-storage/room?hl=zh-cn