安卓系列之Jetpack架构(AAC):Room基础篇

365 阅读6分钟

安卓系列之 Jetpack 架构(AAC):Room 基础篇

Google 官方推荐数据库,ORM(Object Relational Mapping)对象关系映射。

引用

dependencies {
    def room_version = "2.3.0"
    //基础配置
    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"
}

导出 json 文件的配置

json 文件---用于记录数据库升级的日志信息,便于观察数据库升级所更改的内容。

//指定数据库schema导出的位置
javaCompileOptions {
    annotationProcessorOptions {
        arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
    }
}

官方 room 框架图

基础知识

Entity

实体类,即对应表中的一行数据。

字段意义说明
@Entity实体在实体的 class 文件上注解
tableName表名数据表的名称
@PrimaryKey主键每个实体至少有一个主键
(autoGenerate = true)自动分配 ID常用于 ID,自增长
primaryKeys = {"userId"}复合主键
@ColumnInfo列名自定义列名
@Ignore忽略的字段或者构造方法忽略,不生成在数据表里
ignoredColumns忽略父类字段实体继承父类,且不用该字段

Dao

封装访问数据库的方法,例如增删改查。

字段意义说明
@DaoDAO 类封装访问方法
@Insert增加新增一条或者多条数据
@Update更新更新表中数据(需提供主键)
@Delete删除删除表中数据(需提供主键)
@Query查询查询表中数据

RoomDatabase

数据库持有者,内包含 Dao 的抽象对象方法,数据库升级方法,日志记录方法等。

实例

build.gradle 文件

  1. 在 android 的 defaultConfig 下新增数据库 json 日志文件输出地址配置:
//指定数据库schema导出的位置
javaCompileOptions {
    annotationProcessorOptions {
        arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
    }
}
  1. 在 android 下配置 Databinding:
dataBinding {
    enabled = true
}
  1. 在 dependencies 下引入 room 和 baseAdapter 第三方:
//room
implementation 'androidx.room:room-runtime:2.2.6'
annotationProcessor 'androidx.room:room-compiler:2.2.6'
//baseAdapter (辅助列表展示的Adapter,不是数据库内容)
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'

MusicBean 文件

建立实体 Bean 文件,内含表名,自增主键 Id,列名等数据。

package com.elaine.testroom.db.music;

import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;

/**
 * 音乐实体
 * author: elaine
 * date: 2021/7/9
 * <p>
 * Entity -----标记实体
 * tableName = "elaine_music" -----表名
 */
@Entity(tableName = "elaine_music")
public class MusicBean {
    /**
     * 自增长ID,主键
     * PrimaryKey ---主键
     * autoGenerate = true ---自增长
     */
    @PrimaryKey(autoGenerate = true)
    private Long id;
    /**
     * 歌曲名称
     */
    private String name;
    /**
     * mp3文件大小
     */
    private int size;
    /**
     * 评论
     */
    private String comment;
    /**
     * 歌手
     */
    private String singer;
    /**
     * 是否收藏了
     * ColumnInfo -----列信息
     * name = "is_save" -----重命名列名
     * typeAffinity = ColumnInfo.INTEGER -----存储类型
     */
    @ColumnInfo(name = "is_save", typeAffinity = ColumnInfo.INTEGER)
    private boolean isSave;

    /**
     * 是否在播放中
     * Ignore -----忽略,即不写入数据库
     */
    @Ignore
    private boolean isPlay;

    /**
     * 构造方法
     */
    public MusicBean(String name, int size, String comment, String singer, boolean isSave) {
        this.name = name;
        this.size = size;
        this.comment = comment;
        this.singer = singer;
        this.isSave = isSave;
    }

    /**
     * 构造方法
     * Ignore -----忽略,即不写入数据库
     */
    @Ignore
    public MusicBean(String name, int size, String comment, String singer) {
        this.name = name;
        this.size = size;
        this.comment = comment;
        this.singer = singer;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public String getSinger() {
        return singer;
    }

    public void setSinger(String singer) {
        this.singer = singer;
    }

    public boolean isSave() {
        return isSave;
    }

    public void setSave(boolean save) {
        isSave = save;
    }

    public boolean isPlay() {
        return isPlay;
    }

    public void setPlay(boolean play) {
        isPlay = play;
    }
}

MusicDao 文件

数据库相关操作,封装对 MusiceBean 对象的增删改查操作。

package com.elaine.testroom.db.music;

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 java.util.List;

/**
 * 音乐表Dao
 * author: elaine
 * date: 2021/7/9
 */
@Dao
public interface MusicDao {
    /**
     * 插入数据
     *
     * @param musicBeans 实体
     */
    @Insert
    void insert(MusicBean... musicBeans);

    /**
     * 更新
     *
     * @param musicBeans 实体
     */
    @Update
    void update(MusicBean... musicBeans);

    /**
     * 删除
     *
     * @param musicBeans 实体
     */
    @Delete
    void delete(MusicBean... musicBeans);

    /**
     * 获取全部数据
     *
     * @return List<MusicBean>
     */
    @Query("SELECT * FROM elaine_music")
    LiveData<List<MusicBean>> getAll();
}

MusicRepository 文件

异步操作封装。

package com.elaine.testroom.db.music;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;

import androidx.lifecycle.LiveData;

import com.elaine.testroom.db.CommonDatabase;

import java.util.List;

/**
 * 数据库异步处理
 * author: elaine
 * date: 2021/7/12
 */
public class MusicRepository {
    private MusicDao musicDao;

    public MusicRepository(Context context) {
        musicDao = CommonDatabase.getInstance(context).getMusicDao();
    }

    /**
     * 插入数据
     * 异步执行
     *
     * @param musicBeans 实体
     */
    @SuppressLint("StaticFieldLeak")
    public void insert(MusicBean... musicBeans) {
        new AsyncTask<MusicBean, Void, Void>() {

            @Override
            protected Void doInBackground(MusicBean... musicBeans) {
                musicDao.insert(musicBeans);
                return null;
            }
        }.execute(musicBeans);
    }

    /**
     * 更新
     * 异步执行
     *
     * @param musicBeans 实体
     */
    @SuppressLint("StaticFieldLeak")
    public void update(MusicBean... musicBeans) {
        new AsyncTask<MusicBean, Void, Void>() {

            @Override
            protected Void doInBackground(MusicBean... musicBeans) {
                musicDao.update(musicBeans);
                return null;
            }
        }.execute(musicBeans);
    }

    /**
     * 删除
     * 异步执行
     *
     * @param musicBeans 实体
     */
    @SuppressLint("StaticFieldLeak")
    public void delete(MusicBean... musicBeans) {
        new AsyncTask<MusicBean, Void, Void>() {

            @Override
            protected Void doInBackground(MusicBean... musicBeans) {
                musicDao.delete(musicBeans);
                return null;
            }
        }.execute(musicBeans);
    }

    /**
     * 获取全部数据
     * 结合LiveData,方便数据及时更新
     *
     * @return List<MusicBean>
     */
    public LiveData<List<MusicBean>> getAll() {
        return musicDao.getAll();
    }

}

CommonDatabase 文件

数据库文件,内含数据库升级,dao 配置等操作。

package com.elaine.testroom.db;

import android.content.Context;

import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;

import com.elaine.testroom.db.music.MusicBean;
import com.elaine.testroom.db.music.MusicDao;

/**
 * author: elaine
 * date: 2021/7/12
 * <p>
 * entities = MusicBean.class -----对象
 * version = 1 -----当前数据库版本号
 * exportSchema = true  -----是否生成json数据库升级日志文件
 */
@Database(entities = MusicBean.class, version = 1, exportSchema = true)
public abstract class CommonDatabase extends RoomDatabase {
    //数据库名字
    private static final String DATABASE_NAME = "elaine_room";
    //mInstance
    public static CommonDatabase mInstance;

    //单例
    public static CommonDatabase getInstance(Context context) {
        if (mInstance == null) {
            synchronized (CommonDatabase.class) {
                mInstance = Room.databaseBuilder(context.getApplicationContext(), CommonDatabase.class, DATABASE_NAME)
//                            .addMigrations(AppDataBase.MIGRATION_1_2) //若升级则加入这行代码
                        .build();
            }
        }
        return mInstance;
    }

    //若需要升级则添加以下代码。数据库变动添加Migration,简白的而说就是版本1到版本2改了什么东西,
    public static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            database.execSQL("CREATE TABLE `elaine_search_history` (`history` TEXT NOT NULL , `id` LONG PRIMARY KEY AUTOINCREMENT NOT NULL)");
        }
    };

    /**
     * 获取Dao
     *
     * @return MusicDao
     */
    public abstract MusicDao getMusicDao();
}

item_music.xml 文件

recyclerview 的 item 文件,用于展示数据,内含删除和更新按钮。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="bean"
            type="com.elaine.testroom.db.music.MusicBean" />

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="5dp">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{bean.name}"
                android:textColor="@color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="15dp"
                android:text="@{bean.comment}"
                android:textColor="#333333"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="15dp"
                android:text="@{bean.singer}"
                android:textColor="#666666"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="15dp"
                android:text="@{String.valueOf(bean.size)+`MB`}"
                android:textColor="#666666"
                android:textSize="16sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="15dp"
                android:text="@{bean.save ? `已收藏`: `未收藏`}"
                android:textColor="#666666"
                android:textSize="16sp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <Button
                android:id="@+id/btn_delete"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="删除" />

            <Button
                android:id="@+id/btn_update"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:text="更新" />
        </LinearLayout>


    </LinearLayout>
</layout>

activity_main.xml 文件

MainAcitity 的布局文件,内含 recyclerview 和新增按钮。

<?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">

    <data>

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="insert"
            android:text="新增" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_music"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </LinearLayout>

</layout>

MusicAdapter 文件

列表适配器文件。

package com.elaine.testroom;

import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.viewholder.BaseDataBindingHolder;
import com.elaine.testroom.databinding.ItemMusicBinding;
import com.elaine.testroom.db.music.MusicBean;

import org.jetbrains.annotations.NotNull;

/**
 * 列表适配器
 * author: elaine
 * date: 2021/7/9
 */
public class MusicAdapter extends BaseQuickAdapter<MusicBean, BaseDataBindingHolder<ItemMusicBinding>> {
    public MusicAdapter() {
        super(R.layout.item_music);
    }

    @Override
    protected void convert(@NotNull BaseDataBindingHolder<ItemMusicBinding> baseDataBindingHolder, MusicBean bean) {
        ItemMusicBinding mBinding = baseDataBindingHolder.getDataBinding();
        if (mBinding != null) {
            if (bean != null) {
                mBinding.setBean(bean);
            }
            mBinding.executePendingBindings();
        }
    }
}

MainViewModel 文件

继承 AndroidViewModel,内含增删改查方法。

package com.elaine.testroom;

import android.app.Application;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;

import com.elaine.testroom.db.music.MusicBean;
import com.elaine.testroom.db.music.MusicRepository;

import java.util.List;

/**
 * author: elaine
 * date: 2021/7/12
 */
public class MainViewModel extends AndroidViewModel {
    private MusicRepository musicRepository;

    public MainViewModel(@NonNull Application application) {
        super(application);
        musicRepository = new MusicRepository(application);
    }

    /**
     * 插入数据
     *
     * @param musicBeans 实体
     */
    public void insert(MusicBean... musicBeans) {
        musicRepository.insert(musicBeans);
    }

    /**
     * 更新
     *
     * @param musicBeans 实体
     */
    public void update(MusicBean... musicBeans) {
        musicRepository.update(musicBeans);
    }

    /**
     * 删除
     *
     * @param musicBeans 实体
     */
    public void delete(MusicBean... musicBeans) {
        musicRepository.delete(musicBeans);
    }

    /**
     * 获取全部数据
     *
     * @return List<MusicBean>
     */
    public LiveData<List<MusicBean>> getAll() {
        return musicRepository.getAll();
    }
}

MainActivity 文件

package com.elaine.testroom;

import android.os.Bundle;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;

import com.elaine.testroom.databinding.ActivityMainBinding;
import com.elaine.testroom.db.music.MusicBean;

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding mBinding;
    private MainViewModel model;
    private MusicAdapter musicAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        initViewModel();
        initAdapter();
    }

    /**
     * 初始化viewModel
     */
    private void initViewModel() {
        model = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MainViewModel.class);
        //列表进行更改数据
        model.getAll().observe(this, musicBeans -> {
            if (musicBeans != null) {
                musicAdapter.setList(musicBeans);
            }
        });
    }

    /**
     * 初始化适配器
     */
    private void initAdapter() {
        musicAdapter = new MusicAdapter();
        mBinding.rvMusic.setLayoutManager(new LinearLayoutManager(this));
        mBinding.rvMusic.setAdapter(musicAdapter);

        //子控件点击事件
        musicAdapter.addChildClickViewIds(R.id.btn_delete, R.id.btn_update);
        musicAdapter.setOnItemChildClickListener((adapter, view, position) -> {
            MusicBean musicBean = musicAdapter.getData().get(position);
            if (musicBean != null) {
                if (view.getId() == R.id.btn_delete) {//删除操作
                    model.delete(musicBean);
                } else if (view.getId() == R.id.btn_update) {//更新操作
                    musicBean.setSinger("歌手改" + (int) (Math.random() * 10));
                    model.update(musicBean);
                }
            }
        });
    }

    /**
     * 新增按钮点击事件
     */
    public void insert(View view) {
        MusicBean musicBean = new MusicBean("歌曲名称" + (int) (Math.random() * 10), (int) (Math.random() * 10), "评论", "歌手" + (int) (Math.random() * 10), !(Math.random() > 0.5));
        model.insert(musicBean);
    }
}

数据库效果图

界面效果图

数据库相关知识点

关键字

字段说明
DELETE删除
FROM从哪张表
WHERE条件判断
and多个条件并列
SELECT查询
order by按照什么排序
desc倒序
limit限制数量
set赋值
UPDATE更新
CREATE创建
TABLE
NOT NULL不为空
PRIMARY KEY主键
AUTOINCREMENT自增长
ALTER操作已有表
ADD COLUMN增加列
DEFAULT默认值
INSERT INTO插入
DROP TABLE删除表
RENAME TO重命名

查询相关

  1. 查询指定表中的全部数据
SELECT * FROM 表名
  1. 根据 Id 查询指定表中的对象
SELECT * FROM 表名 WHERE id = :id
  1. 根据指定字段查询指定表中的对象
SELECT * FROM 表名 WHERE 字段= :变量
  1. 根据指定多个字段查询指定表中的对象
SELECT * FROM 表名 WHERE 字段= :变量 and 字段= :变量
  1. 根据指定字段查询指定数量的对象,并按照 id 倒序排序,eg.第一页
SELECT * FROM 表名 WHERE 字段= :变量 order by id desc limit 0,:pageSize(变量)
  1. 根据指定字段和 id<变量,查询指定数量的对象,并按照 id 倒序排序,eg.下一页
SELECT * FROM 表名 WHERE 字段= :变量 and id< :变量 order by id desc limit :pageSize (变量)
  1. 根据指定字段查询指定表中一共有多少条数据
SELECT count(*) FROM 表名 WHERE 字段= :变量
  1. 根据指定字段查询指定表中是否已存在,id>0 表示存在
SELECT id FROM 表名 WHERE 字段= :变量
  1. 根据制定字段查询指定表中某个字段是否是 ture,或者是否等于某个值
SELECT 字段(字段= :变量) FROM 表名 WHERE 字段= :变量

删除相关

  1. 删除指定表中全部数据
DELETE FROM 表名
  1. 根据 Id 删除指定表中的对象
DELETE FROM 表名 WHERE id= :id
  1. 根据指定字段删除指定表中的对象
DELETE FROM 表名 WHERE 字段= :变量
  1. 根据指定多个字段删除指定表中的对象
DELETE FROM 表名 WHERE 字段= :变量 and 字段= :变量

更新相关

  1. 根据指定字段更新指定表中的某个数据
UPDATE 表名 set 字段=:变量 WHERE 字段= :变量

项目 github 地址

github.com/ElaineTaylo…

若帅哥美女对该系列文章感兴趣,可微信搜索公众号(木子闲集)关注更多更新文章哦,谢谢~