宁波模板建站哪家服务专业,建筑二级建造师培训机构,网站ico制作,地方网站域名用全拼简述
Android Jetpack的出现统一了Android开发生态#xff0c;各种三方库逐渐被官方组件所取代。Room也同样如此#xff0c;逐渐取代竞品成为最主流的数据库ORM框架。这当然不仅仅因为其官方身份#xff0c;更是因为其良好的开发体验#xff0c;大大降低了SQLite的使用门槛…简述
Android Jetpack的出现统一了Android开发生态各种三方库逐渐被官方组件所取代。Room也同样如此逐渐取代竞品成为最主流的数据库ORM框架。这当然不仅仅因为其官方身份更是因为其良好的开发体验大大降低了SQLite的使用门槛。
Room是Google官方在SQLite基础上封装的一款数据持久库是Jetpack全家桶的一员和Jetpack其他库有着可以高度搭配协调的天然优势。Room使用APT技术大大简化了使用SQLite的代码量只需使用注解配合少量代码即可实现高效的数据库操作。
Room基本介绍
框架特点
相对于SQLiteOpenHelper等传统方法使用Room操作SQLite有以下优势
编译期的SQL语法检查开发高效避免大量模板代码API设计友好容易理解可以与RxJava、 LiveData 、 Kotlin Coroutines等进行桥接
添加依赖
dependencies {implementation androidx.room:room-runtime:2.2.5kapt androidx.room:room-compiler:2.2.5
}基本组件
Room的使用主要涉及以下3个组件
Database: 访问底层数据库的入口Entity: 代表数据库中的表table一般用注解Data Access Object (DAO): 数据库访问者
这三个组件的概念也出现在其他ORM框架中有过使用经验的同学理解起来并不困难 通过Database获取DAO然后通过DAO查询并获取entities最终通过entities对数据库table中数据进行读写
实例实战
insert使用注解 InsertRoom 会自动将所有参数在单个事物中插入数据库
Insert
public fun inertUser (user: User) // 单个参数可以返回 long
Insert
public fun insertUserList (array: ArrayUser) // 参数为集合可以返回 long []
数据库添加 User
val user User()
user.name 赵云 编号 $number
val address Address()
address.street 成都接头
address.state 蜀汉
address.city 常山
address.postCode 10010
user.address address
userDao.inertUser (user) // 添加 User
添加数据结果
upadte使用 Update 注解
Update
public fun update (user: User) // 可以让此方法返回一个 int 值表示数据库中更新的行数 val user User()
user.id 1
user.name 张翼德
address.city 涿郡
.....
userDao.update(user)
点击 Update 后再查询结果此时的赵云已经改为张翼徳了
delete使用 Delete 注解
Delete
public fun delete (user: User) // 可以返回一个 int 值表示从数据库中删除的行数val user User()
user.id 1 // 要删除的主键 id
userDao.delete(user)
点击 delete 后再次查询数据编号为 1 的数据已被删除
查询信息 Query 注解对数据库执行读 / 写操作
Query(SELECT * FROM user)
public fun selectAll (): ArrayUser // 查询所有数据Query(SELECT * FROM user WHERE name :name)
public fun selectUser (name:String): ArrayUser // 条件查询
返回列的子集创建子类在每个属性中使用 ColumnInfo (name name) 标记对应数据库中的列名
public class UserTuple { // 1、根据要查询的字段创建 POJO 对象 ColumnInfo(name name)public var name: String? nullColumnInfo(name city)public var city: String? null
}Query (SELECT name city FROM user) // 2、查询的结果会映射到创建的对象中public ListUserTuple loadFullName();val userList userDao.loadFullName()
for (userTuple in userList) {stringBuilder.append(userTuple.name).append( ).append(userTuple.city).append(\n)
}
输出的结果只有 name 和 city 两列
范围条件查询 查询城市中所有用户
Query(SELECT name ,street FROM user WHERE city IN :cityArray)
fun loadUserInCity(cityArray: ArrayString): ListUserTupleval userList userDao.loadUserInCity (arrayOf (常山)) // 查询常山只会出现赵云不会出现张翼德
Observable 查询使用 LiveData 作为查询方法的返回值注册观察者后数据表更改时自动更新 UI
Query(SELECT name ,street FROM user WHERE city IN :cityArray)
fun loadUserInCityLive(cityArray: ArrayString): LiveDataListUserTuple
private lateinit var liveData: LiveDataArrayUserTuple // 定义一个 LiveData
get() {
return userDao.loadUserInCityLive (arrayOf (常山))
}
val observer ObserverArrayUserTuple { // 定义一个观察者val stringBuilder StringBuilder()for (index in it!!.indices) {val userTuple it[index]stringBuilder.append(userTuple.name).append( ).append(userTuple.name).append( \n)}tv_main_show.text stringBuilder.toString()
}
liveData.observe (this, observer) // 注册观察者
运行结果此时当添加数据时UI 会自动更新
RxJava 查询 返回 Observable 实例可以使用 RxJava 订阅观察者
Query(SELECT * FROM user WHERE id :id LIMIT 1)
fun loadUserRxJava(id:Int) : FlowableUser
userDao.loadUserRxJava(4).subscribe(Consumer {val stringBuilder StringBuilder()stringBuilder.append(it.id).append( ).append(it.name).append( \n)tv_main_show.text stringBuilder.toString()})
Cursor 查询返回 Cursor 对象
fun loadUserCursor(id:Int) : Cursor
多表查询根据表的外键多表查询
Query(SELECT user.name AS userName, pet.name AS petName FROM user, pet WHERE user.id pet.user_id)源码分析
从上面的Demo代码可以看出Room有很多的注解实际上Room正是通过APT注解处理器自动生成了许多代码避免使用者在为了使用数据库编写重复的模板代码。
1.Room.databaseBuilder.build()
数据库使用的入口也就是构造RoomDatabase的实例它里面会根据buider中做的配置进行一系列赋值操作生成一个 DatabaseConfiguration对象然后传入的 class 对象调用Class.newInstance()方法获取到RoomDatabase的实例。 接着就是调用RoomDatabase.init进行初始化操作。
public T build() {//...省略DatabaseConfiguration configuration new DatabaseConfiguration(mContext,mName,factory,mMigrationContainer,mCallbacks,mAllowMainThreadQueries,mJournalMode.resolve(mContext),mQueryExecutor,mTransactionExecutor,mMultiInstanceInvalidation,mRequireMigration,mAllowDestructiveMigrationOnDowngrade,mMigrationsNotRequiredFrom,mCopyFromAssetPath,mCopyFromFile,mCopyFromInputStream,mPrepackagedDatabaseCallback,mTypeConverters);T db Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);db.init(configuration);return db;
}2.RoomDatabase.init()
调用了createOpenHelper方法createOpenHelper方法实现在AppDatabase_Impl中创建了RoomOpenHelperRoomOpenHelper继承SupportSQLiteOpenHelper.Callback。 引申注意这里的 AutoCloser对象这里没有仔细研究它的代码但是它应该是代理持有了一个 SupportSQLiteOpenHelper对象可以实现在提交数据库事务之后自动的判断并close数据库。 从这里就可以看出Room实际上是对 SQLite的再次封装但是通过 APT 以及其他辅助类使得Room的比直接用SQLite要简便很多。
Overrideprotected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {final SupportSQLiteOpenHelper.Callback _openCallback new RoomOpenHelper(configuration,new RoomOpenHelper.Delegate(2) {//...省略}
}
CallSuper
public void init(NonNull DatabaseConfiguration configuration) {mOpenHelper createOpenHelper(configuration);
// Configure SqliteCopyOpenHelper if it is available:SQLiteCopyOpenHelper copyOpenHelper unwrapOpenHelper(SQLiteCopyOpenHelper.class,mOpenHelper);if (copyOpenHelper ! null) {copyOpenHelper.setDatabaseConfiguration(configuration);}
AutoClosingRoomOpenHelper autoClosingRoomOpenHelper unwrapOpenHelper(AutoClosingRoomOpenHelper.class, mOpenHelper);
if (autoClosingRoomOpenHelper ! null) {mAutoCloser autoClosingRoomOpenHelper.getAutoCloser();mInvalidationTracker.setAutoCloser(mAutoCloser);} // ... 省略
} 3.AppDatabase.getUserDao()
AppDatabase是我们继承自RoomDatabase的抽象类我们在里面添加了获取UserDao的方法Room 会通过APT技术在编译之后自动帮我们添加对应的实现。
Overridepublic UserDao getUerDao() {if (_userDao ! null) {return _userDao;} else {synchronized(this) {if(_userDao null) {_userDao new UserDao_Impl(this);}return _userDao;}}}4.UserDao_Impl
可以看出来UserDao_Impl根据注解具体的实现了我们在 UserDao接口中添加的方法其中的关键点就在于根据我们添加在注解中的 sql 语句生成了对数据库的访问代码。
Override
public void insertUserRecord(final User user) {__db.assertNotSuspendingTransaction();__db.beginTransaction();try {__insertionAdapterOfUser.insert(user);__db.setTransactionSuccessful();} finally {__db.endTransaction();}
}
Override
public User geUerById(final String id) {final String _sql select *from User where id (?);final RoomSQLiteQuery _statement RoomSQLiteQuery.acquire(_sql, 1);int _argIndex 1;if (id null) {_statement.bindNull(_argIndex);} else {_statement.bindString(_argIndex, id);}__db.assertNotSuspendingTransaction();final Cursor _cursor DBUtil.query(__db, _statement, false, null);try {final int _cursorIndexOfId CursorUtil.getColumnIndexOrThrow(_cursor, id);final int _cursorIndexOfName CursorUtil.getColumnIndexOrThrow(_cursor, name);final User _result;if(_cursor.moveToFirst()) {final String _tmpId;if (_cursor.isNull(_cursorIndexOfId)) {_tmpId null;} else {_tmpId _cursor.getString(_cursorIndexOfId);}final String _tmpName;if (_cursor.isNull(_cursorIndexOfName)) {_tmpName null;} else {_tmpName _cursor.getString(_cursorIndexOfName);}_result new User(_tmpId,_tmpName);} else {_result null;}return _result;} finally {_cursor.close();_statement.release();}
}总结
本文只是对Room的一个简单分析正如在一开始的概述里面说的那样Room是对SQLite数据库的抽象它提供了很多便利的API和注解等简化了使用者使用数据库的方式。本文没有分析 Room和 LiveData结合使用的情况因为笔者公司的项目还没能引入 LiveData。
抛开这一点不谈个人认为它有两个比较显著的优点 1、当然就是通过APT技术TypeConverter 等简化了使用减少了大量的访问数据库的模板代码。 2、是文中也有提到的它对数据库升级的优化除了可以避免两个分支同时升级数据库但是合并不冲突导致的错误之外它还提供了对数据库升级做单元测试的工具类安全性提升很多。