Room提供了一个基于SQLite的抽象层,以便在利用SQLite的全部功能的同时实现更强大的数据库访问。
Room可帮助您在运行应用程序的设备上创建应用程序数据的缓存。此缓存作为应用程序的唯一事实来源,允许用户在应用程序中查看关键信息的一致副本,无论用户是否具有网络连接。
Room 主要组成包括三个部分
Database: 包含数据库持有者,并作为应用程序持久关系数据的基础连接的主要访问点。
@Database 注解的类应满足以下条件:
- 是一个扩展的抽象类 RoomDatabase。
- 在注解中包括与数据库关联的Entity列表。
- 包含一个具有无参的抽象方法,并返回带注释的类 @Dao.
Entity:表示数据库中的表。
DAO:包含用于访问数据库的方法。
三者之间的关系 :
使用
环境需求:Android Studio 2.3 或更高版本
添加依赖 :
implementation 'android.arch.persistence.room:runtime:1.1.1'
annotationProcessor 'android.arch.persistence.room:compiler:1.1.1'
//如果配合rxjiava使用需要添加
implementation 'io.reactivex.rxjava2:rxjava:2.2.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
具体示例
创建实体类
Repo :
@Entity //添加Entity 表示数据库中的表
public class Repo {
@PrimaryKey //主键
public int id;
public String name;
public String url;
public int age;
public boolean isNew;
public Repo() {
age = 1;
}
public Repo(int id, String name, String url) {
this.id = id;
this.name = name;
this.url = url;
age=0;
}
//getter and setter 省略
......
@Override
public String toString() {
return "Repo{" +
"id=" + id +
", name='" + name + '\'' +
", url='" + url + '\'' +
", age=" + age +
", isNew=" + isNew +
'}';
}
}
创建Dao
RepoDao
@Dao
public interface RepoDao {
@Query("SELECT * FROM Repo")
List<Repo> getAllRepos();
@Insert(onConflict = REPLACE)
void insert(Repo... repos);
@Update
void update(Repo... repos);
@Delete
void delete(Repo... repos);
@Query("SELECT*FROM REPO WHERE id = :id")
Repo qurery(int id);
}
创建数据库(Database)
RepoDatabase
@Database(entities = Repo.class, version = 3)
public abstract class RepoDatabase extends RoomDatabase {
private static final String DB_NAME = "repoDatabase.db";
private static volatile RepoDatabase instance;
static synchronized RepoDatabase getInstance(Context context) {
if (instance == null) {
instance = create(context);
}
return instance;
}
private static RepoDatabase create(final Context context) {
return Room.databaseBuilder(
context,
RepoDatabase.class,
DB_NAME)
.build();
}
public abstract RepoDao getRepoDao();
}
注意:由于创建 RoomDatabase 示例比较耗费资源,如果您的应用程序在单个进程中运行,则在实例化AppDatabase对象时应遵循单例设计模式。
这样整个数据库就创建完成了,接下来就是对数据库操作了。
需要注意的是:
对数据库的所有操作需要在子线程中
对数据库的所有操作需要在子线程中
对数据库的所有操作需要在子线程中
这里使用Rxjava 配合线程切换使用。把相关的具体操作封装在一个RepoUtils类中 :
public class RepoUtils {
public void insert(final Context context, final Repo repo) {
Observable.create(new ObservableOnSubscribe<Void>() {
@Override
public void subscribe(ObservableEmitter<Void> emitter) throws Exception {
RepoDatabase
.getInstance(context)
.getRepoDao()
.insert(repo);
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe();
}
public Observable<List<Repo>> getAll(final Context context) {
return Observable.create(new ObservableOnSubscribe<List<Repo>>() {
@Override
public void subscribe(ObservableEmitter<List<Repo>> emitter) {
emitter.onNext(RepoDatabase
.getInstance(context).getRepoDao().getAllRepos());
emitter.onComplete();
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
public Observable<Repo> query(final Context context, final int repo) {
return Observable.create(new ObservableOnSubscribe<Repo>() {
@Override
public void subscribe(ObservableEmitter<Repo> emitter) {
Repo qurery = RepoDatabase.getInstance(context).getRepoDao().qurery(repo);
if (qurery == null) {
Log.d("MainActivity", "this id:" + repo + "don't exist");
return;
}
emitter.onNext(qurery);
emitter.onComplete();
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
public void update(final Context context, final Repo repo) {
Observable.create(new ObservableOnSubscribe<Void>() {
@Override
public void subscribe(ObservableEmitter<Void> emitter) {
RepoDatabase.getInstance(context).getRepoDao().update(repo);
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe();
}
在MainActivity 中进行测试:
public class MainActivity extends AppCompatActivity {
RepoUtils reposity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
reposity = new RepoUtils();
findViewById(R.id.insert).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Repo repo = new Repo();
repo.setId(1);
repo.setName("");
repo.setUrl("Tomurl");
reposity.insert(MainActivity.this, repo);
}
});
findViewById(R.id.getAll).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Disposable subscribe = reposity.getAll(MainActivity.this).subscribe(new Consumer<List<Repo>>() {
@Override
public void accept(List<Repo> repos) throws Exception {
Log.d("MainActivity", repos.toString());
}
});
}
});
findViewById(R.id.insertSame).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Repo repo = new Repo();
repo.setId(1);
repo.setName("JERRY");
reposity.insert(MainActivity.this, repo);
}
});
findViewById(R.id.queryExist).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Disposable mainActivity = reposity.query(MainActivity.this, 1).subscribe(new Consumer<Repo>() {
@Override
public void accept(Repo repo) {
Log.d("MainActivity", repo.toString());
}
});
}
});
findViewById(R.id.queryNoExist).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Disposable mainActivity = reposity.query(MainActivity.this, 4).subscribe(new Consumer<Repo>() {
@Override
public void accept(Repo repo) {
Log.d("MainActivity", repo.toString());
}
});
}
});
findViewById(R.id.update).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Repo repo = new Repo();
repo.setId(1);
repo.setName("WO");
repo.setUrl("Tomurl");
reposity.update(MainActivity.this, repo);
}
});
}
}
insert: 插入一个ID=1 ,name="", url =“Tomurl”
insertSame: 插入一个ID=1,name=“JERRY” url=null
getAll: 获取数据库中的所有数据
queryExist :获取ID=1 的一条数据
queryNoExist:获取一个不存在的ID
update:更新ID=1 的数据
log输出:
MainActivity: [Repo{id=1, name='', url='Tomurl', age=1, isNew=false}] //insert
MainActivity: [Repo{id=1, name='JERRY', url='null', age=1, isNew=false}] //insertSame
MainActivity: Repo{id=1, name='JERRY', url='null', age=1, isNew=false} //getAll
MainActivity: [Repo{id=1, name='JERRY', url='null', age=1, isNew=false}] //queryExist
MainActivity: this id:4don't exist //queryNoExist
MainActivity: [Repo{id=1, name='WO', url='Tomurl', age=1, isNew=false}] //update getAll
从上面的操作和打印日志可以我们在回到RepoDap 中
@Insert(onConflict = REPLACE)
void insert(Repo... repos);
@Update
void update(Repo... repos);
如果有相同的ID的数据,冲突解决办法
OnConflictStrategy.REPLACE //替换老数据
OnConflictStrategy.ROLLBACK // 回滚事务
OnConflictStrategy.ABORT //放弃事务
OnConflictStrategy.FAIL //失败事务
OnConflictStrategy.IGNORE //忽略冲突
如遇到数据库升级问题可以参考另一篇文章Room 数据库升级问题 java.lang.IllegalStateException: Migration didn’t properly handle xxx希望能够帮助到您。