Room设计到的概念有以下几个:
1、Entity : 对应数据库中的表,可以使用Entity注解将一个类变成数据库中的一张表结构。
2、DAO : 全称Database Access Object,定义了对数据库中数据的读写等操作,DAO中可以使用SQL语句来操作数据库。
3、RoomDatabase : 数据库持有类,用于创建数据库或者连接到数据库。内部包含DAO和Entity。
首先进行build.gradle的配置
android {
...
//提供dataBinding的支持
dataBinding.enabled true
//配置当Database中exportSchema=true时,生成的数据结构文件存储位置。
javaCompileOptions {
annotationProcessorOptions {
arguments =["room.schemaLocation":"$projectDir/schemas".toString()]
}
}
}
dependencies {
...
//添加lifecycle框架的依赖
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-rc02'
//添加Room数据库框架的依赖
def room_version = "2.2.2"
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"
implementation 'androidx.recyclerview:recyclerview:1.1.0'
}
Room Database中的Entity表示一张数据表结构,一个Entity实例就是表中的一行,如定义一个Word类的Entity。
重点:
1、一个Entity对象代表数据表中的一行,一个Entity类代表一张数据表。
2、Entity中的成员变量都是数据表中的列。
3、一个Java类定义成Entity只要加上Entity注解就可以了。
①创建实体类,用于定义数据表中需要存储的字段;
package com.mrlove.roombasic.domain;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
//entity声明定义,并且指定了映射数据表明
@Entity
public class Word {
//设置主键自动增长
@PrimaryKey(autoGenerate = true)
private int id;
//字段映射具体的数据表字段名,如果没有定义则用属性名,作为列名。
@ColumnInfo(name = "word")
private String word;
private String chineseMeaning;
//必须指定一个构造方法,room框架需要。并且只能指定一个
//,如果有其他构造方法,则其他的构造方法必须添加@Ignore注解
public Word(String word, String chineseMeaning) {
this.word = word;
this.chineseMeaning = chineseMeaning;
}
//Setter、Getter方法是需要添加的,为了支持room工作
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
public String getChineseMeaning() {
return chineseMeaning;
}
public void setChineseMeaning(String chineseMeaning) {
this.chineseMeaning = chineseMeaning;
}
}
在DAO(data access object)中,可以使用SQL语句进行对数据库的操作并且将这些语句与Java中方法关联调用,编译器会检查SQL语句并且通过注解生成对应的查询语句,例如@Insert。
注意:
1、DAO必现是抽象类或者接口
2、所有的查询语句必须在单独的线程里面执行。
②接着我们要创建具体的Dao
package com.mrlove.roombasic.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import com.mrlove.roombasic.domain.Word;
import java.util.List;
//注解配置sql语句
@Dao //Database access object
public interface WordDao {
@Insert
void insertWords(Word... words); //...表示可以传递多个同类型参数
@Update
void updateWords(Word... words);
@Delete
void deleteWords(Word... words);
@Query("DELETE FROM WORD") //查询语句写的清空所有数据
void deleteAllWords();
@Query("SELECT * FROM Word ORDER BY id DESC")
LiveData<List<Word>> getAllWords(); //设置数据为可观察类型LiveData
}
上面已经介绍了DAO和Entity,Entity定义表,DAO定义对表中数据进行操作,RoomDatabase包含了DAO,并且提供创建和连接数据库的方法。
创建Room database包括三个步骤:
1、创建继承RoomDatabase的抽象类。
2、在继承的类前使用注解@Database。
3、申明数据库结构的Entity,并且设置数据库的版本号。
注意:
1、编译时会检查SQL语句是否正确
2、不要在主线程中进行数据库操作
3、RoomDatabase最好使用单例模式
③然后自定义类继承RoomDatabase
package com.mrlove.roombasic.dao;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import com.mrlove.roombasic.domain.Word;
//配置你要操作的entry类,可以配置一个或者多个,version表名这个是哪个版本,如果升级需要修改的就是这里
@Database(entities = {Word.class}, version = 1) //exportSchema 默认为true,存储展示数据库的结构信息
public abstract class WordDataBase extends RoomDatabase {
//singleton
private static WordDataBase wordDataBase; //单例模式,保证获取的数据库实例是唯一的
public static synchronized WordDataBase getWordDataBase(Context context) {
if (wordDataBase == null) {
wordDataBase = Room.databaseBuilder(context.getApplicationContext(), WordDataBase.class, "word_database")
//下面注释表示允许主线程进行数据库操作,但是不推荐这样做。
//他可能造成主线程lock以及anr
//所以我们的操作都是在新线程完成的
// .allowMainThreadQueries()
.build();
}
return wordDataBase;
}
//RoomDatabase提供直接访问底层数据库实现,我们通过定义抽象方法返回具体Dao
//然后进行数据库增删该查的实现。
public abstract WordDao getWordDao();
}
至此,数据库创建,连接,操作部分已经完成。
④现在创建mainactivity文件
MainActivity.java
package com.mrlove.roombasic;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.Observer;
import androidx.lifecycle.SavedStateViewModelFactory;
import androidx.lifecycle.ViewModelProvider;
import com.mrlove.roombasic.databinding.ActivityMainBinding;
import com.mrlove.roombasic.domain.Word;
import java.util.List;
public class MainActivity extends AppCompatActivity {
MyViewModel myViewModel;
//DatabindingBinding 由框架编译时生成,负责通知界面同步更新(命名方式:xml文件名 + Binding);
ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//DataBindingUtil 将布局文件与Activity关联,生成DatabindingBinding实例binding;
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
//获取ViewModel实例
myViewModel = new ViewModelProvider(this,new SavedStateViewModelFactory(getApplication(),this)).get(MyViewModel.class);
//为布局文件设置源数据
binding.setData(myViewModel);
//为实现LiveData的数据设置观察者,以便当数据改变时通知UI更新数据
myViewModel.getAllWordsLive().observe(this, new Observer<List<Word>>() {
@Override
public void onChanged(List<Word> words) {
//用StringBuilder保证在内存中只创建了一个对象,而String += ...,在每次+的时候实际都会创建一个新的对象。
StringBuilder str = new StringBuilder();
for (Word word: words
) {
str.append(word.getId()).append(":").append(word.getWord()).append("=").append(word.getChineseMeaning()).append("\n");
}
binding.textView2.setText(str.toString());
}
});
//添加
binding.buttoninsert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Word word1 = new Word("hello","你好");
Word word2 = new Word("world","世界");
myViewModel.insertWords(word1,word2);
}
});
//删除所有
binding.buttonclear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
myViewModel.clearWords();
}
});
//更新
binding.buttonupdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Word word = new Word("haha","哈哈");
word.setId(40);
myViewModel.updateWords(word);
}
});
//删除某一项
binding.buttondelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Word word = new Word("haha","哈哈");
word.setId(40);
myViewModel.deleteWords(word);
}
});
}
}
⑤创建ViewModel
package com.mrlove.roombasic;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import com.mrlove.roombasic.domain.Word;
import java.util.List;
public class MyViewModel extends AndroidViewModel {
private WordRepository wordRepository;
public MyViewModel(@NonNull Application application) {
super(application);
wordRepository = new WordRepository(application); //初始化得到wordRepository工具类
}
LiveData<List<Word>> getAllWordsLive() {
return wordRepository.getAllWordslive();
}
void insertWords(Word... words) {
wordRepository.insertWords(words);
}
void updateWords(Word... words) {
wordRepository.updateWords(words);
}
void deleteWords(Word... words) {
wordRepository.deleteWords(words);
}
void clearWords() {
wordRepository.clearWords();
}
}
⑥创建WordRepository,这是一个工具类,主要的作用是减少ViewModel的代码量,让ViewModel专注与逻辑的实现,而对数据的具体操作则由工具类提供,让代码结构层次更加清晰。
package com.mrlove.roombasic;
import android.content.Context;
import android.os.AsyncTask;
import androidx.lifecycle.LiveData;
import com.mrlove.roombasic.dao.WordDao;
import com.mrlove.roombasic.dao.WordDataBase;
import com.mrlove.roombasic.domain.Word;
import java.util.List;
public class WordRepository {
private LiveData<List<Word>> allWordsLive;
private WordDao wordDao;
public WordRepository(Context context) {
//获取数据库实例(唯一)
WordDataBase wordDataBase = WordDataBase.getWordDataBase(context.getApplicationContext());
//获取数据库操作dao的实例
wordDao = wordDataBase.getWordDao();
//获取数据库的所有数据
//LiveData格式的数据在获取时,系统自动会调用Async来处理
allWordsLive = wordDao.getAllWords();
}
//为实现AsyncTask静态内部类提供访问的接口
void insertWords(Word... words){
new InsertAsyncTask(wordDao).execute(words);
}
void updateWords(Word... words){
new UpdateAsyncTask(wordDao).execute(words);
}
void deleteWords(Word... words){
new DeleteAsyncTask(wordDao).execute(words);
}
void clearWords(){
new ClearAsyncTask(wordDao).execute();
}
LiveData<List<Word>> getAllWordslive() {
return allWordsLive;
}
//把对数据库的操作封装到实现AsyncTask的类中,因为Room对数据库的操作是耗时操作,不允许在主线程中执行。
static class InsertAsyncTask extends AsyncTask<Word,Void,Void>{
private WordDao wordDao;
InsertAsyncTask(WordDao wordDao) {
this.wordDao = wordDao;
}
@Override
protected Void doInBackground(Word... words) {
wordDao.insertWords(words);
return null;
}
}
static class ClearAsyncTask extends AsyncTask<Void,Void,Void>{
private WordDao wordDao;
ClearAsyncTask(WordDao wordDao) {
this.wordDao = wordDao;
}
@Override
protected Void doInBackground(Void... Void) {
wordDao.deleteAllWords();
return null;
}
}
static class UpdateAsyncTask extends AsyncTask<Word,Void,Void>{
private WordDao wordDao;
UpdateAsyncTask(WordDao wordDao) {
this.wordDao = wordDao;
}
@Override
protected Void doInBackground(Word... words) {
wordDao.updateWords(words);
return null;
}
}
static class DeleteAsyncTask extends AsyncTask<Word,Void,Void>{
private WordDao wordDao;
DeleteAsyncTask(WordDao wordDao) {
this.wordDao = wordDao;
}
@Override
protected Void doInBackground(Word... words) {
wordDao.deleteWords(words);
return null;
}
}
}
这里是布局文件的实现
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="data"
type="com.mrlove.roombasic.MyViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.6" />
<ScrollView
android:id="@+id/scrollView2"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0">
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textSize="30sp"
tools:text="@string/textview" />
</ScrollView>
<Button
android:id="@+id/buttoninsert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/buttoninsert"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/buttonupdate"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline3"
app:layout_constraintVertical_bias="0.151" />
<Button
android:id="@+id/buttonupdate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/buttonupdate"
app:layout_constraintBottom_toBottomOf="@+id/buttoninsert"
app:layout_constraintEnd_toStartOf="@+id/buttondelete"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/buttoninsert" />
<Button
android:id="@+id/buttondelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/buttondelete"
app:layout_constraintBottom_toBottomOf="@+id/buttonupdate"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/buttonupdate" />
<Button
android:id="@+id/buttonclear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="68dp"
android:text="@string/buttonclear"
app:layout_constraintStart_toStartOf="@+id/buttonupdate"
app:layout_constraintTop_toBottomOf="@+id/buttonupdate" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
至此一个实现Room数据库框架的简单Demo搭建完成。
参考博客:
https://www.jianshu.com/p/72c8efc3ad87
https://blog.csdn.net/u013309870/article/details/86515074
官方示例创建Demo:
https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#0
github参考代码:
https://github.com/Mrlove133481/RoomBasic