Room,数据库框架学习一(Room框架搭建)

概念

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'
}

Entity

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

在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
}

RoomDatabase

上面已经介绍了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

发布了29 篇原创文章 · 获赞 1 · 访问量 842

猜你喜欢

转载自blog.csdn.net/u011609120/article/details/104036236