Android ROOM的基本使用
简介
room持久层提供一个SQLite的抽象层
Room不是数据库,只是对Sqlite做了一个改造
Room 是一个对象关系映射(ORM)库。可以很容易将 SQLite 表数据转换为 Java 对象。Room 在编译时检查 SQLite 语句。
Room 为 SQLite 提供一个抽象层,以便在充分利用 SQLite 的同时,可以流畅地进行数据库访问。
导入依赖
dependencies {
def room_version = “1.1.1”
implementation "android.arch.persistence.room:runtime:$room_version"
annotationProcessor "android.arch.persistence.room:compiler:$room_version" // use kapt for Kotlin
// optional - RxJava support for Room
implementation "android.arch.persistence.room:rxjava2:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "android.arch.persistence.room:guava:$room_version"
// Test helpers
testImplementation "android.arch.persistence.room:testing:$room_version"
}
ROOM的三个重要组件
在使用数据的时候,需要主要涉及到Room三个部分:
- Entity: 数据库中表对应的实体
- DataBase: 创建数据库实例
- Dao: 操作数据库的方法
第一步 创建实体类
import android.graphics.Bitmap;
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import java.util.Date;
/**
1. 播放记录
*/
//创建一个实体Entity playRecord,在注释中包含数据库相关联的实体列表
@Entity(tableName = "play_record")//数据库的表名
public class PlayRecord {
// 有些情况下,需要主键是自增的,可以配置属性autoGenerate=true来实现。
// @PrimaryKey(autoGenerate = true)
// @ColumnInfo(name = "_id")
// private int id;
/**
* 文件地址
*/
@PrimaryKey()//主键自增
@NonNull
private String url;
/**
* 播放位置
*/
private int position;
/**
* 记录时间
*/
@ColumnInfo(name = "update_time")//在表中对应的字段名,updateTime是该对象中的变量
private Date updateTime;
//设置忽略字段
@Ignore
Bitmap picture;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
- @Entity: 代表一个表中的实体,默认类名就是表名,如果不想使用类名作为表名,可以给注解添加表名字段
- @Entity(tableName = “user”)
- @PrimaryKey: 每个实体都需要自己的主键
- @NonNull 表示字段,方法,参数返回值不能为空
- @ColumnInfo(name = “faceId”) 如果希望表中字段名跟类中的成员变量名不同,添加此字段指明
第二步 创建Dao
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import com.group10.myapplication.room.entity.PlayRecord;
import java.util.List;
//写入方法,增删改查 url查询地址,带参数
@Dao
public interface PlayRecordDao {
@Insert
long insert(PlayRecord record);
@Update
int update(PlayRecord record);
@Delete
int delete(PlayRecord record);
@Query("DELETE FROM play_record WHERE url = :url")
int deleteByUrl(String url);
@Query("SELECT * FROM play_record")
List<PlayRecord> queryAll();
@Query("SELECT * FROM play_record WHERE url IN (:urls)")
List<PlayRecord> queryByUrls(List<String> urls);
@Query("SELECT * FROM play_record WHERE url = :url")
PlayRecord queryByUrl(String url);
}
DAO是数据访问对象,指定SQL查询,并让他与方法调用相关联。
DAO必须是一个接口或者抽象类。
默认情况下,所有的查询都必须在单独的线程中执行
第三步 创建Database
import androidx.room.Database;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
import com.group10.myapplication.room.converter.Converters;
import com.group10.myapplication.room.dao.PlayRecordDao;
import com.group10.myapplication.room.entity.PlayRecord;
//创建数据库类dateBase
//在room持久库中,通过@Datebase类来访问数据库,注释定义数据库类,entities指明包含的实体
//version 表示版本
@Database(entities = {PlayRecord.class}, version = 1, exportSchema = false)
@TypeConverters(Converters.class)
public abstract class AppDatabase extends RoomDatabase {
/**
* 获取一个播放列表的操作对象
*
* @return
*/
public abstract PlayRecordDao playRecordDao();
}
-
创建一个抽象类继承自appDatabase
-
给他添加一个注解@Database表名它是一个数据库,注解有两个参数第一个是数据库的实体,它是一个数组,可以传多个,当数据库创建的时候,会默认给创建好对应的表,第二个参数是数据库的版本号
-
定义跟数据库一起使用的相关的DAO类
-
创建一个RoomDemoDatabase的单例,防止同时打开多个数据库的实例
-
使用Room提供的数据库构建器来创建该实例,第一个参数application,第二个参数当前数据库的实体类,第三个参数数据库的名字
第四步,获取播放记录操作对象
/**
* 一个工具类:用于获取各种数据库句柄
*/
public class DatabaseHelper {
/**
* 数据库名称
*/
public static final String DATABASE_NAME = "video_player";
//单例模式,保证获取的数据库实例是唯一的
private static AppDatabase AppDatabase;
//数据库版本号
private static final int DATABASE_VERSION = 1;
/**
* 获取播放记录操作对象
* 获得接口的对象在主线程运行
* @param context
* @return
*/
public static PlayRecordDao getPlayRecordDao(Context context) {
AppDatabase db = Room
.databaseBuilder(context, AppDatabase.class, DATABASE_NAME)
.allowMainThreadQueries() // 运行在主线程执行
.build();
return db.playRecordDao();
}
}
第五步 类型转换
当字段为空的时候,数据库会crash,报错信息是说“数据库不可为空的字段为空”,实际上对比sql语句,发现所有的not null字段全部是有值的。
原因是使用了TypeConverter,上面的代码中的转换方式会导致photo字段被系统认为是not null字段了
要解决这个问题,要改为以下这样,入参和返回值都要加?,这样系统认为这个字段是可为空的
import androidx.room.TypeConverter;
import java.util.Date;
//类型转换long类型转换成date类型,进行存储。date类型转换成long
public class Converters {
@TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
@TypeConverter
public static Long dateToTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}
第六步 新增或更新播放记录
@Override
public void onBackPressed() {
if (vgContainerFullScreen.indexOfChild(vVideoArea) != -1) {
fullScreen(false);
} else {
// 新增或更新播放记录
try {
if (null != mp && mp.isPlaying()) {
PlayRecord playRecord = new PlayRecord();
playRecord.setUrl(mediaInfos.get(currentIndex).getUrl());
playRecord.setPosition(mp.getCurrentPosition());
playRecord.setUpdateTime(new Date());
playRecordService.insertOrUpdate(playRecord);
}
} catch (Exception e) {
Log.e(TAG, "playByIndex: ", e);
}
super.onBackPressed();
}
}