Android Jetpack组件之数据库Room详解(二)

本文涉及Library的版本如下:

  • androidx.room:room-runtime:2.1.0-alpha03
  • androidx.room:room-compiler:2.1.0-alpha03(注解编译器)

回顾一下安卓的SQLiteOpenHelper相关类

首先放一个关于安卓数据库的类图:

SQLiteOpenHelper是一个抽象类,通常自己实现数据库,需要继承SQLiteOpenHelper, 在OnCreate()里建表,在onUpgrade()处理版本迁移等。SQLiteOpenHelper是个帮助类,里面SQLiteDatabase类才是真正代表一个数据库。SQLiteDatabase提供了打开数据库,增删改查等方法。我们通常是执行sql语句去操作数据库,只不过官方封装好更方便使用。

上图中SQLiteProgram是抽象类,是编译SQLite程序的基类。成员变量里有sql语句、表字段名数据,相对应的字段的值。SQLiteStatement继承SQLiteProgram, 提供一下执行语句的方法。SQLiteQurey也是继承SQLiteProgram,代表了查询的执行操作。安卓的数据库操作把查询操作和其他操作分开。通过SQLiteDirectCursorDriver驱动执行SQLiteQurey生成SQLiteCursor游标来去数据; 建表,删表,建索引等是通过SQLiteStatement.excute()执行; 更新和删除通过SQLiteStatement.executeUpdateDelete()执行; 插入数据通过SQLiteStatement.executeInsert()。Room在原有的基础上进行了封装。

Room的类图结构

上图有一些Support开头的接口, 这些接口存在androidx.sqlite:sqlite:2.0.0库里, 这个是对安卓原有数据库操作做了接口的抽离。SupportSQLiteDatabase对应SQLiteDatabase,、SupportSQLiteOpenHelper对应SQLiteOpenHelper、SupportSQLiteProgram对应SQLiteProgram等等;

Framework开头一些类的是对一些Support接口的实现;Framework开头这些类存在于androidx.sqlite:sqlite-framework:2.0.0库中, FrameworkSQLiteDatabase实现里有个成员变量SQLiteDatabase,实现的接口都是交给SQLiteDatabase处理。FrameworkSQLiteOpenHelper、FrameworkSQLiteProgram、FrameworkSQLiteStatement都是这个套路,使用装饰者模式,真正的实现还是安卓原有数据库操作的类。FrameworkSQLiteOpenHelperFactory工厂返回得是FrameworkSQLiteOpenHelper.OpenHelper类,FrameworkSQLiteOpenHelper.OpenHelper继承SQLiteOpenHelper。

Room开头这些类存在androidx.room:room-runtime:2.10库中, 这个库基于Support这类接口(例如:SupportSQLiteDatabase)和Framework实现(FrameworkSQLiteDatabase的实现)再次封装。room使用过程中需要定义一个抽象AppDatabase继承RoomDatabase,使用Room.databaseBuilder()去生成AppDatabase的实现。

Room数据库表的创建

AppDatabase是一个抽象类,真正的实现是AppDatabase_Impl, AppDatabase_Impl是用编译时注解生成的,注解编译器是androidx.room:room-compiler:$room_version。AppDatabase实现类是由RoomDatabase.Builder.build()创建的,先看build方法的实现:

public T build() {......if (mFactory == null) { // SQLiteOpenHelper工厂mFactory = new FrameworkSQLiteOpenHelperFactory();}//DatabaseConfiguration是数据配置类,//存储context, 数据库名,SQLiteOpenHelperFactory等参数DatabaseConfiguration configuration =new DatabaseConfiguration(mContext, mName, mFactory, mMigrationContainer,mCallbacks, mAllowMainThreadQueries, mJournalMode.resolve(mContext),mQueryExecutor,mMultiInstanceInvalidation,mRequireMigration,mAllowDestructiveMigrationOnDowngrade, mMigrationsNotRequiredFrom);//DB_IMPL_SUFFIX = "_Impl", db的实现是AppDatabase_ImplT db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);//init方法实现在RoomDatabasedb.init(configuration);return db;
}static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {final String fullPackage = klass.getPackage().getName();String name = klass.getCanonicalName();final String postPackageName = fullPackage.isEmpty()? name: (name.substring(fullPackage.length() + 1));final String implName = postPackageName.replace('.', '_') + suffix;// klass的全包名 + "_Impl",反射调用newInstance()生成实例final Class<T> aClass = (Class<T>) Class.forName(fullPackage.isEmpty() ? implName : fullPackage + "." + implName);return aClass.newInstance();
}//RoomDatabase.init方法
public void init(@NonNull DatabaseConfiguration configuration) {//建表mOpenHelper = createOpenHelper(configuration);boolean wal = false;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {wal = configuration.journalMode == JournalMode.WRITE_AHEAD_LOGGING;//是否打开日志mOpenHelper.setWriteAheadLoggingEnabled(wal);}mCallbacks = configuration.callbacks;// 查询Executor:决定查询执行的线程mQueryExecutor = configuration.queryExecutor; mAllowMainThreadQueries = configuration.allowMainThreadQueries;mWriteAheadLoggingEnabled = wal;if (configuration.multiInstanceInvalidation) {mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,configuration.name);}
}复制代码

RoomDatabase.createOpenHelper方法是抽象方法,实现在AppDatabase_Impl, 定位到AppDatabase_Impl.createOpenHelper方法

  @Overrideprotected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {//_openCallback是局部匿名内部实例, 是一个RoomOpenHelper实例, 先不管这个实例,接着看下面。final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {@Overridepublic void createAllTables(SupportSQLiteDatabase _db) {//执行建表语句_db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`first_name` TEXT, `name` TEXT, `id` INTEGER NOT NULL, PRIMARY KEY(`id`))");}@Overrideprotected void onCreate(SupportSQLiteDatabase _db) {if (mCallbacks != null) {for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {mCallbacks.get(_i).onCreate(_db);}}}@Overridepublic void onOpen(SupportSQLiteDatabase _db) {mDatabase = _db;internalInitInvalidationTracker(_db);if (mCallbacks != null) {for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {mCallbacks.get(_i).onOpen(_db);}}}}, "e216f2ddb0b894667088e1e7fec58cdd", "07bca20d2ba295fc9d4acbe7a3f64d4b");//SupportSQLiteOpenHelper.Configuration数据库相关配置//存储了context, name(数据库名字),SupportSQLiteOpenHelper.Callback对象final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context).name(configuration.name).callback(_openCallback).build();//工厂生成SupportSQLiteOpenHelper, 这里的工厂默认是FrameworkSQLiteOpenHelperFactory//默认值在RoomDatabase.build()里赋值final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);return _helper;}//FrameworkSQLiteOpenHelperFactory类的实现
public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {@Overridepublic SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {//new 一个FrameworkSQLiteOpenHelper, 接着看FrameworkSQLiteOpenHelper的构造器//获取SupportSQLiteOpenHelper.Configuration的context, name, callbackreturn new FrameworkSQLiteOpenHelper(configuration.context, configuration.name, configuration.callback);}
}//FrameworkSQLiteOpenHelper的构造器
FrameworkSQLiteOpenHelper(Context context, String name,Callback callback) {mDelegate = createDelegate(context, name, callback);
}
//FrameworkSQLiteOpenHelper.createDelegate方法
private OpenHelper createDelegate(Context context, String name, Callback callback) {final FrameworkSQLiteDatabase[] dbRef = new FrameworkSQLiteDatabase[1];//OpenHelper是FrameworkSQLiteOpenHelper的内部类return new OpenHelper(context, name, dbRef, callback);
}//OpenHelper继承安卓的SQLiteOpenHelper
static class OpenHelper extends SQLiteOpenHelper {final FrameworkSQLiteDatabase[] mDbRef;final Callback mCallback;// see b/78359448private boolean mMigrated;OpenHelper(Context context, String name, final FrameworkSQLiteDatabase[] dbRef,final Callback callback) {super(context, name, null, callback.version,new DatabaseErrorHandler() {@Overridepublic void onCorruption(SQLiteDatabase dbObj) {FrameworkSQLiteDatabase db = dbRef[0];if (db != null) {callback.onCorruption(db);}}});mCallback = callback;mDbRef = dbRef;}@Overridepublic void onCreate(SQLiteDatabase sqLiteDatabase) {//数据库初始化, mCallback是RoomOpenHelpermCallback.onCreate(getWrappedDb(sqLiteDatabase));}
}   //RoomOpenHelper.onCreate方法
@Override
public void onCreate(SupportSQLiteDatabase db) {//更新Identity(这里跳过,不深入)updateIdentity(db);//mDelegate是RoomOpenHelper的内部类Delegate, Delegate是一个抽象类,//定义了createAllTables, onCreate, onOpen等方法//mDelegate在这里实现是AppDatabase_Impl.createOpenHelper方法里_openCallback实例//调用建表方法mDelegate.createAllTables(db);//调用onCreate方法mDelegate.onCreate(db);//回看_openCallback实例的实现, createAllTables里执行了建表语句
}复制代码

Room数据库插入操作过程

Room数据访问只需要定义一个DAO接口, 在Dao接口里定义方法以及注解即可。

@Dao
public interface UserDao {@Insert(onConflict = OnConflictStrategy.REPLACE)void insert(User user);
}//调用
AppDatabase.getInstance().userDao().insert(user);
复制代码

前面有说到AppDatabase实现类AppDatabase_Impl, 而这里UserDao的实现类是UserDao_Impl。UserDao_Impl也是编译时自动生成实现类,先看一下AppDatabase_Impl.userDao()

  @Overridepublic UserDao userDao() {if (_userDao != null) {return _userDao;} else {synchronized(this) {if(_userDao == null) {_userDao = new UserDao_Impl(this);}return _userDao;}}}
复制代码

接口UserDao_Impl的实现

//UserDao_Impl构造器
public UserDao_Impl(RoomDatabase __db) {this.__db = __db;// 创建一个EntityInsertionAdapter对象this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {//插入的数据的sql语句@Overridepublic String createQuery() {return "INSERT OR ABORT INTO `User`(`first_name`,`name`,`id`) VALUES (?,?,?)";}//绑定插入的字段,对应上面sql语句的‘?’占位符@Overridepublic void bind(SupportSQLiteStatement stmt, User value) {if (value.firstName == null) {stmt.bindNull(1); // 对应第一个“?”占位符,代表更新first_name的值} else {stmt.bindString(1, value.firstName);}if (value.name == null) {stmt.bindNull(2);} else {stmt.bindString(2, value.name);}stmt.bindLong(3, value.id);}};
}    @Overridepublic void insert(final User user) {__db.beginTransaction();try { // 开启事务,进行插入数据__insertionAdapterOfUser.insert(user);__db.setTransactionSuccessful();} finally {__db.endTransaction();}}
复制代码

从上面的代码可以知道,最后开启一个事务,调用EntityInsertionAdapter.insert方法进行数据插入, EntityInsertionAdapter是一个抽象类,利用泛型封装了支持所有类型的数据插入,实现类必须要实现createQuery和bind两个方法。createQuery方法返回一条sql语句,而bind方法是对数据类进行绑定,和sql语句是一一对应的。

EntityInsertionAdapter.insert方法如下:

public final void insert(T entity) {final SupportSQLiteStatement stmt = acquire(); //创建一个SupportSQLiteStatement, 这里会调用到createQuery方法try {bind(stmt, entity);//会调用bind(SupportSQLiteStatement stmt, T value)stmt.executeInsert();//执行sql语句} finally {release(stmt);}}
复制代码

最终数据类的插入,通过SupportSQLiteStatement接口去执行sql语句。看回本篇文章开头的类结构图,SupportSQLiteStatement是一个接口, 实现是FrameworkSQLiteStatement, 而FrameworkSQLiteStatement内部实现是有一个委派者SQLiteStatement去执行的,最终也是调用安卓原有SQLiteStatement类去执行。

Room数据库查询过程

查询也类似,通过定义一个Dao接口和@Query注解,大致代码如下:

@Dao
public interface UserDao {@Query("SELECT * FROM user WHERE id = :id")User findById(String id);
}    
复制代码

UserDao_Impl.findById实现如下:

  @Overridepublic User findById(final String id) {final String _sql = "SELECT * FROM user WHERE id = ?";//通过sql创建SQLite查询执行程序final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);int _argIndex = 1;if (id == null) {_statement.bindNull(_argIndex);} else {_statement.bindString(_argIndex, id);//绑定参数id}final Cursor _cursor = DBUtil.query(__db, _statement, false);//执行查询语句//下面通过cursor获取数据并赋值给Usertry {final int _cursorIndexOfFirstName = CursorUtil.getColumnIndexOrThrow(_cursor, "first_name");final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");final User _result;if(_cursor.moveToFirst()) {_result = new User();_result.firstName = _cursor.getString(_cursorIndexOfFirstName);_result.name = _cursor.getString(_cursorIndexOfName);_result.id = _cursor.getInt(_cursorIndexOfId);} else {_result = null;}return _result;} finally {_cursor.close();_statement.release();}}
复制代码

总结

  • Room对安卓原有的数据类相关类进行一次封装,对SQLiteOpenHelper, SQLiteDatabase, SQLiteProgram,SQLiteStatement,SQLiteQurey抽象出了对应的接口SupportSQLiteOpenHelper, SupportSQLiteDatabase, SupportSQLiteProgram, SupportSQLiteStatement, SupportSQLiteQurey。然后提供了一套Framework字母开头的系列类是对Support接口的实现。
  • 抽象出一系列Support接口,可以给开发者去实现一套不是基于sqlite数据库。例如可以替代sqlite的开源库realm
  • Room是一个基于DAO架构数据库框架,利用apt编译时自动生成代码来实现大量模块代码

转载于:https://juejin.im/post/5cb552f3e51d456e6f45c6e4

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/387851.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像识别中的深度学习

来源&#xff1a;《中国计算机学会通讯》第8期《专题》 作者&#xff1a;王晓刚 深度学习发展历史 深度学习是近十年来人工智能领域取得的重要突破。它在语音识别、自然语言处理、计算机视觉、图像与视频分析、多媒体等诸多领域的应用取得了巨大成功。现有的深度学习模型属于神…

多个css样式合并到一个“目录”css文件中

执行访问jsp后发现没有效果 同样的代码&#xff0c;在html中效果对比如下&#xff1a; 具体原因&#xff1a;不清楚&#xff0c;暂时记着~~~在jsp中不支持import这种css样式的引用 转载于:https://www.cnblogs.com/mangwusuozhi/p/10050108.html

Git 学习笔记之 merge

Merge: 1、Fast-forward&#xff08;快进式&#xff09; 2、recursice strategy (策略合并&#xff0c;三方合并) Fast-forward 策略合并 //创建一个文件夹&#xff0c;并初始化 Git mkdir GitDemo cd GitDemo git init//初次提交&#xff0c;创建 master 分支 touch master.tx…

熊猫直播 使用什么sdk_没什么可花的-但是16项基本操作才能让您开始使用熊猫

熊猫直播 使用什么sdkPython has become the go-to programming language for many data scientists and machine learning researchers. One essential data processing tool for them to make this choice is the pandas library. For sure, the pandas library is so versat…

萌新一手包App前后端开发日记(一)

从事Android移动端也有些日子了&#xff0c;还记得一开始选择这份工作&#xff0c;是憧憬着有朝一日能让亲朋好友用上自己开发的软件&#xff0c;但日子久了才发现&#xff0c;并不是所有的公司&#xff0c;所有的项目的适用群体都是“亲朋好友”&#xff0c;/无奈脸 摊手。当…

方差,协方差 、统计学的基本概念

一、统计学的基本概念 统计学里最基本的概念就是样本的均值、方差、标准差。首先&#xff0c;我们给定一个含有n个样本的集合&#xff0c;下面给出这些概念的公式描述&#xff1a; 均值&#xff1a; 标准差&#xff1a; 方差&#xff1a; 均值描述的是样本集合的中间点&#xf…

关系型数据库的核心单元是_核中的数据关系

关系型数据库的核心单元是Nucleoid is an open source (Apache 2.0), a runtime environment that provides logical integrity in declarative programming, and at the same time, it stores declarative statements so that it doesn’t require external database, in shor…

MongoDB第二天

集合的操作: db.表名称 show tables / collection db.表名.drop() 文档的操作: 插入数据 db.表名.insert({"name":"jerry"}) db.insertMany([{"name":"sb",...}]) var ul {"name":"sb"} db.sb.insert(ul) db.sb.…

Python 主成分分析PCA

Python 主成分分析PCA 主成分分析&#xff08;PCA&#xff09;是一种基于变量协方差矩阵对数据进行压缩降维、去噪的有效方法&#xff0c;PCA的思想是将n维特征映射到k维上&#xff08;k<n&#xff09;&#xff0c;这k维特征称为主元&#xff0c;是旧特征的线性组合&#xf…

小程序 国际化_在国际化您的应用程序时忘记的一件事

小程序 国际化The hidden bugs waiting to be found by your international users您的国际用户正在等待发现的隐藏错误 While internationalizing our applications, we focus on the things we can see: text, tool-tips, error messages, and the like. But, hidden in our …

三. 性能测试领域

能力验证&#xff1a; 概念&#xff1a;系统能否在A条件下具备B能力 应用&#xff1a;为客户进行系统上线后的验收测试&#xff0c;作为第三方对一个已经部署系统的性能验证 特点&#xff1a;需要在已确定的环境下运行 需要根据典型场景设计测试方案和用例 一个典型场景包括操…

PCA主成分分析Python实现

作者&#xff1a;拾毅者 出处&#xff1a;http://blog.csdn.net/Dream_angel_Z/article/details/50760130 Github源码&#xff1a;https://github.com/csuldw/MachineLearning/tree/master/PCA PCA&#xff08;principle component analysis&#xff09; &#xff0c;主成分分…

scp

将文件或目录从本地通过网络拷贝到目标端。拷贝目录要带 -r 参数 格式&#xff1a;scp 本地用户名IP地址:文件名1 远程用户名IP地址:文件名 2 例&#xff1a; scp media.repo root192.168.20.32:/etc/yum.repos.d/ 将远程主机文件或目录拷贝到本机&#xff0c;源和目的参数调换…

robo 3t连接_使用robo 3t studio 3t连接到地图集

robo 3t连接Robo 3T (formerly Robomongo) is a graphical application to connect to MongoDB. The newest version now includes support for TLS/SSL and SNI which is required to connect to Atlas M0 free tier clusters.Robo 3T(以前称为Robomongo )是用于连接MongoDB的…

JavaWeb--JavaEE

一、JavaEE平台安装1、升级eclipseEE插件2、MyEclipse二、配置Eclipse工作空间1.字体设置 2.工作空间编码 UTF-83.JDK版本指定 4.集成Tomcat Server运行环境5.配置server webapps目录 端口号 启动时间等三、创建第一个Web项目1.创建 Web Project2.设置 tomcat、创建web.xml3.目…

软件需求规格说明书通用模版_通用需求挑战和机遇

软件需求规格说明书通用模版When developing applications there will be requirements that are needed on more than one application. Examples of such common requirements are non-functional, cookie consent and design patterns. How can we work with these types of…

python版PCA(主成分分析)

python版PCA&#xff08;主成分分析&#xff09; 在用统计分析方法研究这个多变量的课题时&#xff0c;变量个数太多就会增加课题的复杂性。人们自然希望变量个数较少而得到的信息较多。在很多情形&#xff0c;变量之间是有一定的相关关系的&#xff0c;当两个变量之间有一定…

干货|Spring Cloud Bus 消息总线介绍

2019独角兽企业重金招聘Python工程师标准>>> 继上一篇 干货&#xff5c;Spring Cloud Stream 体系及原理介绍 之后&#xff0c;本期我们来了解下 Spring Cloud 体系中的另外一个组件 Spring Cloud Bus (建议先熟悉 Spring Cloud Stream&#xff0c;不然无法理解 Spr…

一类动词二类动词三类动词_基于http动词的完全无效授权技术

一类动词二类动词三类动词Authorization is a basic feature of modern web applications. It’s a mechanism of specifying access rights or privileges to resources according to user roles. In case of CMS like applications, it needs to be equipped with advanced l…

主成份分析(PCA)详解

主成分分析法&#xff08;Principal Component Analysis&#xff09;大多在数据维度比较高的时候&#xff0c;用来减少数据维度&#xff0c;因而加快模型训练速度。另外也有些用途&#xff0c;比如图片压缩&#xff08;主要是用SVD&#xff0c;也可以用PCA来做&#xff09;、因…