Android ORM框架实现探索

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大会上推出的官方数据库框架。参考网上的资料总结二者的对比:

GreenDaoRoom
开发者只需要规定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语句实现了方法。到这我们就简单的过了一遍基本功能的实现。

MiniOrm-for-Android是什么? MiniOrm-for-android  是一款简单,只能,灵活的android ORM框架,完全基于对象进行操作。主要帮助android程序员的快速开发。通过反射将查询的数据智能的转换成 Entity 。省去开发人员手动解析的时间。 功能特点: 小巧,类库大小27k 快速,10000条数据,时长3.6秒 使用简单,支持实体类注解方式,除了实体类之外只需创建一个DAO就可以进行操作。 支持原声的sql语句操作 耦合性低 结构模型: 类关系图: 纯java结构设计,通过接口的设计,将上层业务和实际操作数据库的部分进行分离,开发者可以在此基础上自行实现mysql等数据的支持。下面是java部分核心设计 接入类库: 在项目的程序目录build.gradle中加入 dependencies {      compile 'com.ml.miniorm:miniorm:1.1.8'  } 基本方法: 创建表: StuDao stuDao=new StuDao(); stuDao.createTable();  新增: Student student=new Student();  student.setAge(2);  student.setStuName("王小明"); new StuDao().save(student);  删除:    //1. 根据主键删除student.setId(2);new StudentDao().delete(student); //2.根据其他属性删除 student.setStuName("kkkk");     student.setAge(2); stuDao.delete(student); //3.删除全部 stuDao.deleteAll(); 更新:  // (注意,需指定ID) student.setId(2); student.setStuName("kkkk"); stuDao.update(student); 查询: //全部 List<Student> list=stuDao.queryAll(); //query  by id stuDao.queryById(1)||stuDao.queryById(“1” 精确条件查询 可以写sql语句 然后直接调用方法 teacherDao.executeQuery("select * from usertable",teacherDao.getQueryEntity(),teacherDao.getReflexEntity() ); 调用QueryBuilder //查询 全部 性别为 女 List listaaa=teacherDao.getQueryBuilder().callQuery().queryAll().where(Where.handle().eq("sex","女")).executeQueryList(); List<Teacher> listaaa=teacherDao.getQueryBuilder().callQuery().queryAll().where(Where.handle().and("sex","=","女")).executeQueryList(); //模糊查询 List<CustomerBean> list = customerBeanDao.getQueryBuilder().callQuery().queryAll().where(Where.handle().and("userName", " like ", "%" tiaojian "%").or().and("company", " like ", "%" tiaojian "%").or().and("nickname", " like ", "%" tiaojian "%")).executeQueryList(); //对应的 sql select * from CustomerBean where userName like '%你曾%' or company like '%你曾%' or nickname like '%你曾%' ; //根据主键分页 int lastid = teacherDao.queryLastInsertId(); String table = teacherDao.getReflexEntity().getTableEntity().getTableName(); String column = teacherDao.getReflexEntity().getTableIdEntity().getColumnName(); List list1 = teacherDao.getQueryBuilder().callQuery().queryAll().where(Where.handle().and(column, "<=", lastid).and(column, ">", lastid - 标签:MiniOrm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值