本文涉及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

Android Jetpack组件之数据库Room详解(二)相关推荐

  1. Android Jetpack组件之数据库Room详解(三)

    本文涉及Library的版本如下: androidx.room:room-runtime:2.1.0-alpha03 androidx.room:room-compiler:2.1.0-alpha03 ...

  2. 基于sqlite的android数据库编程,Android编程之SQLite数据库操作方法详解

    Android编程之SQLite数据库操作方法详解 发布时间:2020-09-07 12:33:04 来源:脚本之家 阅读:85 作者:低调小一 本文实例讲述了Android SQLite数据库操作方 ...

  3. Android四大组件Service之AIDL详解

    Android四大组件Service之AIDL详解 前言 简介 基础知识 AIDL 服务端 定义AIDL文件规则 创建 .aidl 文件 清单注册 通过 IPC 传递对象 调用 IPC 方法 Andr ...

  4. 安卓 linux init.rc,[原创]Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

  5. Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

  6. flutter 获取android 还是ios_Flutter完整开发实战详解(二十、 Android PlatformView 和键盘问题)...

    作为系列文章的第二十篇,本篇将结合官方的技术文档科普 Android 上 PlatformView 的实现逻辑,并且解释为什么在 Android 上 PlatformView 的键盘总是有问题. 为什 ...

  7. android线性布局设置控件固定在底部,Android UI组件LinearLayout线性布局详解

    LinearLayout 线性布局,该布局的继承关系: 1. 什么是线性布局 通俗的说感觉起来和线有关,参照线的特点,有么是横向的,要么是竖向的. LinearLayout是线性布局控件,它包含的子控 ...

  8. Dedesql数据库类详解(二次开发必备教程)(转)

    http://www.dedecms.com/help/development/2009/1028/1076.html 织梦DedeCMS的二次开发不仅仅是会写写织梦的标签,会制作织梦的模板.很多时候 ...

  9. dede mysql数据库_Dedesql数据库类详解(二次开发必备教程)

    注意:图片为缩略图,点击看大图.更清晰... 今天花点时间讲解下织梦的sql数据库类,近期本来是准备录制一套视频教程的,但由于视频压缩的问题迟迟没有开展工作,如果大家有什么好的视频压缩方式可以通过邮件 ...

最新文章

  1. 通过Java代码实现图片的放大和缩小
  2. mybatis增删改查快速实现!!!
  3. 创建接口匿名实现类的对象的四种方法
  4. MyEclipse 皮肤、主题、背景色
  5. 物主代词-mine、yours_33
  6. BZOJ1061: [Noi2008]志愿者招募(线性规划)
  7. 移动应用开发 jQuery Mobile
  8. 陕西2020行政区划调整_陕西省最新行政区划,厉害了大陕西
  9. Cpk (Process Capability Index)
  10. 推荐 21 款博主常用 Windows 软件
  11. win10系统进行电脑分盘
  12. Redhat8.0安装mariadb
  13. linux中man命令的基本用法,linux中的man命令的详细解释
  14. rabbit 的使用方法
  15. Axure RP 9 for Mac 中文版 专业产品原型设计工具
  16. 名帖205 蔡襄 行书《蔡襄自书诗》
  17. 20190605学习日记
  18. A*算法 JAVA实现
  19. Gate 7.2的学习笔记(一)
  20. 5.Python之input和while

热门文章

  1. Linux系统上的程序调优思路概要
  2. 图像分割——基于二维灰度直方图的阈值处理
  3. Java BufferedReader reset()方法及示例
  4. Python连接MySQL及一系列相关操作
  5. Object类的hashCode()方法
  6. 三菱880彩铅和uni的区别_孟祥雷丨清华美院毕业,彩铅界的“冷军”(附彩铅教程哦!)...
  7. [leetcode] 617. Merge Two Binary Trees
  8. 文件描述符和fcntl及阻塞非阻塞
  9. appium 设置参数
  10. Xshell链接不上云服务器的解决方案