ORM概念
ORM 表示全称为对象关系映射(Object Relational Mapping),简单说,ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术。ORM通过描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
- 数据库的表(table) --> 类(class)
- 字段(field)–> 对象的属性(attribute)
- 记录(record,行数据)–> 对象(object)
Android ORM框架
目前业内有很多ORM框架如GreenDAO,Room等,在使用方式上其实大同小异。
直至目前,GreenDao都无疑是Android SQLite Based ORMs中的带头大哥,无论是Github Star数,还是各类博客中出现的次数,都稳居第一。而谷歌推出的持久化框架Room则有望与之齐平,Room框架是Google在2017年Google IO大会上推出的官方数据库框架。参考网上的资料总结二者的对比:
GreenDao | Room |
---|---|
开发者只需要规定Entity的属性即可 | 需要规定Entity的属性,需要规定Dao接口 |
每次更新Entity需要重新build以生成代码,大型项目build耗时会比较久 | 更新Entity不需要重新build,因为使用的是Dao接口规定的方法,但是需要根据情况更新Dao接口 |
只有进行复杂操作时才需要写SQL语句 | 即使是进行简单的条件查询,也要写SQL语句 |
有一定的学习成本,需要学习注解、查询、条件语句等API | 学习成本低,基本只需要掌握Room的注解即可 |
很明显,如果熟悉SQL语句,那么Room会更容易上手,在开发中也相对更加灵活。而GreenDao对于复杂的查询操作则需要熟练掌握GreenDao包装的各种函数,学习成本略高。另外Room毕竟是Google官方推荐针对Android的数据库框架,相信之后的适配、优化和维护也会更加及时。
Room实现原理解析
先实现下最基本的功能,创建一张表,@Entity
声明了表名,@ColumnInfo
声明了表中字段
@Entity(tableName = "Student")
data class Student(
@PrimaryKey(autoGenerate = true)
var studentID: Int?,
@ColumnInfo(name = "s_name")
var studentName: String?,
@ColumnInfo(name = "s_type")
var studentType: String?
)
再创建数据访问对象层 Dao,在这里我们需要定义一些对数据库增删改查的方法。新建一个接口类用@Dao
注解,之后APT会帮我们生成接口的实现类。这里可以把通用的方法抽成BaseDao:
@Dao
interface StudentDao : BaseDao<Student> {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(element: Student)
@Query("select * from Student")
fun getAllStudents(): MutableList<Student>
@Query("select * from Student where studentID = :studentID")
fun getStudnet(studentID: Int): Student
@Query("select * from Student order by studentID desc ")
fun getAllByDateDesc(): MutableList<Student>
@Query("delete from Student")
fun deleteAll()
}
最后将实体和 DAO整合在一起的类是 RoomDatabase,先创建一个扩展 RoomDatabase 的抽象类,对它进行注释,声明实体和相应的 DAO和数据库版本号。
@Database(entities = [Student::class], version = 1)
abstract class AppDataBase() : RoomDatabase() {
abstract fun getStudentDao(): StudentDao
companion object {
@Volatile
private var INSTANCE: AppDataBase? = null
fun getInstance(context: Context): AppDataBase =
INSTANCE ?: synchronized(this) {
INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
}
private fun buildDatabase(context: Context) =
Room.databaseBuilder(context.applicationContext, AppDataBase::class.java, "User.db")
.build()
}
}
之后build一下项目,看到build目录下生成了两个文件,是使用APT生成的代码:
之后我们数据库的增删改查都是使用我们创建的数据库对象进行操作,我们以创建对象为入口看下:
Room.databaseBuilder(context.applicationContext, AppDataBase::class.java, "User.db").build()
在看build方法:
private static final String DB_IMPL_SUFFIX = "_Impl";
public T build() {
...
DatabaseConfiguration configuration =
new DatabaseConfiguration(
mContext,
mName,
mFactory,
mMigrationContainer,
mCallbacks,
mAllowMainThreadQueries,
mJournalMode.resolve(mContext),
mQueryExecutor,
mTransactionExecutor,
mMultiInstanceInvalidation,
mRequireMigration,
mAllowDestructiveMigrationOnDowngrade,
mMigrationsNotRequiredFrom,
mCopyFromAssetPath,
mCopyFromFile);
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
db.init(configuration);
return db;
}
在进入getGeneratedImplementation
方法:
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;
//noinspection TryWithIdenticalCatches
try {
@SuppressWarnings("unchecked")
final Class<T> aClass = (Class<T>) Class.forName(
fullPackage.isEmpty() ? implName : fullPackage + "." + implName);
return aClass.newInstance();//反射创建对象
} catch (ClassNotFoundException e) {
throw new RuntimeException("cannot find implementation for "
+ klass.getCanonicalName() + ". " + implName + " does not exist");
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot access the constructor"
+ klass.getCanonicalName());
} catch (InstantiationException e) {
throw new RuntimeException("Failed to create an instance of "
+ klass.getCanonicalName());
}
}
可以看到是通过拼接“_Impl”,使用反射创建了之前APT生成的AppDataBase_Impl
类对象,数据库对象实际使用的是AppDataBase_Impl
对象,我们在看AppDataBase_Impl
中代码:
public final class AppDataBase_Impl extends AppDataBase {
private volatile StudentDao _studentDao;
@Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
@Override
public void createAllTables(SupportSQLiteDatabase _db) {//建表语句
_db.execSQL("CREATE TABLE IF NOT EXISTS `Student` (`studentID` INTEGER PRIMARY KEY AUTOINCREMENT, `s_name` TEXT, `s_type` TEXT)");
_db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
_db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ef8f75ff3e461c15595a53f78ae4a79a')");
}
@Override
public void dropAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("DROP TABLE IF EXISTS `Student`");
if (mCallbacks != null) {
for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
mCallbacks.get(_i).onDestructiveMigration(_db);
}
}
}
@Override
protected void onCreate(SupportSQLiteDatabase _db) {
if (mCallbacks != null) {
for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
mCallbacks.get(_i).onCreate(_db);
}
}
}
@Override
public 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);
}
}
}
@Override
public void onPreMigrate(SupportSQLiteDatabase _db) {
DBUtil.dropFtsSyncTriggers(_db);
}
@Override
public void onPostMigrate(SupportSQLiteDatabase _db) {
}
@Override
protected RoomOpenHelper.ValidationResult onValidateSchema(SupportSQLiteDatabase _db) {
final HashMap<String, TableInfo.Column> _columnsStudent = new HashMap<String, TableInfo.Column>(3);
_columnsStudent.put("studentID", new TableInfo.Column("studentID", "INTEGER", false, 1, null, TableInfo.CREATED_FROM_ENTITY));
_columnsStudent.put("s_name", new TableInfo.Column("s_name", "TEXT", false, 0, null, TableInfo.CREATED_FROM_ENTITY));
_columnsStudent.put("s_type", new TableInfo.Column("s_type", "TEXT", false, 0, null, TableInfo.CREATED_FROM_ENTITY));
final HashSet<TableInfo.ForeignKey> _foreignKeysStudent = new HashSet<TableInfo.ForeignKey>(0);
final HashSet<TableInfo.Index> _indicesStudent = new HashSet<TableInfo.Index>(0);
final TableInfo _infoStudent = new TableInfo("Student", _columnsStudent, _foreignKeysStudent, _indicesStudent);
final TableInfo _existingStudent = TableInfo.read(_db, "Student");
if (! _infoStudent.equals(_existingStudent)) {
return new RoomOpenHelper.ValidationResult(false, "Student(Student).\n"
+ " Expected:\n" + _infoStudent + "\n"
+ " Found:\n" + _existingStudent);
}
return new RoomOpenHelper.ValidationResult(true, null);
}
}, "ef8f75ff3e461c15595a53f78ae4a79a", "9ce15bd5371f52bb9c0814a503c70294");
final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
.name(configuration.name)
.callback(_openCallback)
.build();
final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
return _helper;
}
@Override
protected InvalidationTracker createInvalidationTracker() {
final HashMap<String, String> _shadowTablesMap = new HashMap<String, String>(0);
HashMap<String, Set<String>> _viewTables = new HashMap<String, Set<String>>(0);
return new InvalidationTracker(this, _shadowTablesMap, _viewTables, "Student");
}
@Override
public void clearAllTables() {
super.assertNotMainThread();
final SupportSQLiteDatabase _db = super.getOpenHelper().getWritableDatabase();
try {
super.beginTransaction();
_db.execSQL("DELETE FROM `Student`");
super.setTransactionSuccessful();
} finally {
super.endTransaction();
_db.query("PRAGMA wal_checkpoint(FULL)").close();
if (!_db.inTransaction()) {
_db.execSQL("VACUUM");
}
}
}
@Override
public StudentDao getStudentDao() {
if (_studentDao != null) {
return _studentDao;
} else {
synchronized(this) {
if(_studentDao == null) {
_studentDao = new StudentDao_Impl(this);
}
return _studentDao;
}
}
}
}
可以看到实现了创建表删除表的语句,最后还实现getStudentDao()
方法返回的是生成的StudentDao_Impl
类对象,也就是我们增删改查的方法都有这个类实现,而StudentDao_Impl
类中则根据我们在StudentDao
中定义的操作,使用sql语句实现了方法。到这我们就简单的过了一遍基本功能的实现。