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.pswp.cn/news/387851.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/387851.shtml
英文地址,請注明出處:http://en.pswp.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;、因…