美文网首页
SQLite Room

SQLite Room

作者: Youremywoman | 来源:发表于2019-04-30 10:52 被阅读0次

    Room

    由于SQLiteOpenHelper的接口调用起来比较繁琐。比如插入需要我们自己组装成一个contentValues,查询的时候需要自己将Cursor中的数据取出,这里有很多重复的逻辑,而room就是为了帮助我们解决这些让数据库操作更加便捷,Room是Google官方提供的数据库ORM框架。

    对一个表的操作只要写如下代码

    @Database(entities = {DebugEntity.class},version = 1)
    public abstract class DebugDatabase  extends RoomDatabase {
        public abstract DebugDao debugDao();
    }
    
    @Dao
    public interface DebugDao {
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        void insert(DebugEntity... debugEntities);
    
        @Update
        void update(DebugEntity debugEntity);
    
        @Delete
        void delete(DebugEntity entity);
    
        @Query("SELECT * from tb_debug WHERE id= :id")
        Single<DebugEntity> loadWithId(int id);
    }
    
    
    @Entity(tableName = "tb_debug")
    public class DebugEntity {
        @PrimaryKey
        private long id;
        @ColumnInfo
        private String content;
    
        public DebugEntity() {
        }
    
        public DebugEntity(long id, String content) {
            this.id = id;
            this.content = content;
        }
    
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    }
    
     debugDatabase = Room.databaseBuilder(context, DebugDatabase.class, "debug_database")
                    .build();
    //完成对数据库的操作
    debugDatabase.debugDao().xxx()
    
    1. 有以上DataBase,Dao,Entity三个类,room的compiler(之前写的预编译文章)模块会在编译前帮我们生成类DataBase,Dao_Impl.

    2. @Entity :将一个类标记为entity,这个类用于到数据库表的映射,每个entity至少有一个@PrimaryKey标记的字段,用@Ignore标记不需要持久化的字段,如果一个字段用transient修饰,自动忽略除非用ColunmInfo,@Embedde,@Relation注解.

    3. @Dao: 将一个类标记为数据访问对象,用于定义如何和数据库交互,可以包括各种查询方法,用@Dao注解的类需要是一个接口或者是一个抽象类,如果和@DataBase关联在编译时Room将会生成实现类。建议有多个Dao类用于与多个数据表的交互。

    4. @Database: 将一个类标记为RoomDatabase,这个类需要继承RoomDatabase并是一个抽象类,你可以用

      Room.databaseBuilder,Room.inMemoryDatabaseBuilder得到实现类。在编译时Room会检测你在Dao中写的查询语句,如果有问题会及时的通知你。


    Room时SQLiteHelper上的一层抽象,这次用SQLiteHelper的功能去查看room的各个功能是如何实现的。

    1. 打开数据库:RoomDatabase.mOpenHelper 在RoomDatabase init时 实现类的createOpenHelper创建

      FrameworkSQLiteOpenHelper.getWritableDatabase, FrameworkSQLiteOpenHelper生成一个委托类OpenHelper是SQLiteOpenHelper的子类。后面就是通过OpenHelper调用SQLiteDatabase.getWritableDatabase()打开连接数据库。

    2. 看一下room 创建更新表的逻辑 :OpenHelper.onCreate->RoomOpenHelper.onCreate。room回自己创建一个room_master_table,room回根据Entity在RoomOpenHelper实现类createAllTables生成创建表的逻辑。后面执行databaseBuilder.addCallback加入的Callback回调。更新:升级

    3. 增,删,改。委托于原来的方法流程SQLiteDatabase 创建一个SQLiteStatement预处理SQL语句,绑定查询参数,SQLiteStatement去查询(session->connectionpool->connection)。FrameworkSQLiteDatabase委托于SQLiteDatabase,FrameworkSQLiteStatement委托于SQLiteStatement

    4. 查询。RoomSQLiteQuery(用于缓存绑定参数)和SQLiteQuery没委托关系。查询流程见SQLiteOpenHelper


    SupportSQLiteOpenHelper

    用于映射SQLiteOpenHelper行为的一个接口。

    FrameworkSQLiteDatabase:委托类委托于SQLiteDatabase

    FrameworkSQLiteStatement:委托于SQLiteStatement

    createOpenHelper
    @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 `tb_debug` (`id` INTEGER NOT NULL, `content` TEXT, PRIMARY KEY(`id`))");
            _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, \"11aae594046709aa50c44370e237d950\")");
          }
    
          @Override
          public void dropAllTables(SupportSQLiteDatabase _db) {
            _db.execSQL("DROP TABLE IF EXISTS `tb_debug`");
          }
    
          @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
          protected void validateMigration(SupportSQLiteDatabase _db) {
            final HashMap<String, TableInfo.Column> _columnsTbDebug = new HashMap<String, TableInfo.Column>(2);
            _columnsTbDebug.put("id", new TableInfo.Column("id", "INTEGER", true, 1));
            _columnsTbDebug.put("content", new TableInfo.Column("content", "TEXT", false, 0));
            final HashSet<TableInfo.ForeignKey> _foreignKeysTbDebug = new HashSet<TableInfo.ForeignKey>(0);
            final HashSet<TableInfo.Index> _indicesTbDebug = new HashSet<TableInfo.Index>(0);
            final TableInfo _infoTbDebug = new TableInfo("tb_debug", _columnsTbDebug, _foreignKeysTbDebug, _indicesTbDebug);
            final TableInfo _existingTbDebug = TableInfo.read(_db, "tb_debug");
            if (! _infoTbDebug.equals(_existingTbDebug)) {
              throw new IllegalStateException("Migration didn't properly handle tb_debug(com.ruixue.widelypaycashier.debug.local.DebugEntity).\n"
                      + " Expected:\n" + _infoTbDebug + "\n"
                      + " Found:\n" + _existingTbDebug);
            }
          }
        }, "11aae594046709aa50c44370e237d950", "1382025f9c31fbd26fd351ed296fd598");
        final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
            .name(configuration.name)
            .callback(_openCallback)
            .build();
        final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
        return _helper;
      }
    

    SupportSQLiteOpenHelper.Callback:处理连接数据库生命周期的个个时间,类似SQLiteOpenHelper功能

    SupportSQLiteOpenHelper.Configuration: 用于SupportSQLiteOpenHelper.Factory创建helper的一个配置文件

    sqliteOpenHelperFactory 在RoomDatabase build时传入没有默认为FrameworkSQLiteOpenHelperFactory

    FrameworkSQLiteOpenHelperFactory:用于创建一个FrameworkSQLiteOpenHelper。最终我们得到是一个

    FrameworkSQLiteOpenHelper

    升级
     @Nullable
            public List<Migration> findMigrationPath(int start, int end) {
                if (start == end) {
                    return Collections.emptyList();
                }
                boolean migrateUp = end > start;
                List<Migration> result = new ArrayList<>();
                return findUpMigrationPath(result, migrateUp, start, end);
            }
    
    private List<Migration> findUpMigrationPath(List<Migration> result, boolean upgrade,
                    int start, int end) {
                final int searchDirection = upgrade ? -1 : 1;
                while (upgrade ? start < end : start > end) {
                    SparseArrayCompat<Migration> targetNodes = mMigrations.get(start);
                    if (targetNodes == null) {
                        return null;
                    }
                    // keys are ordered so we can start searching from one end of them.
                    final int size = targetNodes.size();
                    final int firstIndex;
                    final int lastIndex;
    
                    if (upgrade) {
                        firstIndex = size - 1;
                        lastIndex = -1;
                    } else {
                        firstIndex = 0;
                        lastIndex = size;
                    }
                    boolean found = false;
                    for (int i = firstIndex; i != lastIndex; i += searchDirection) {
                        final int targetVersion = targetNodes.keyAt(i);
                        final boolean shouldAddToPath;
                        if (upgrade) {
                            shouldAddToPath = targetVersion <= end && targetVersion > start;
                        } else {
                            shouldAddToPath = targetVersion >= end && targetVersion < start;
                        }
                        if (shouldAddToPath) {
                            result.add(targetNodes.valueAt(i));
                            start = targetVersion;
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        return null;
                    }
                }
                return result;
            }
    
     public void addMigrations(@NonNull Migration... migrations) {
                for (Migration migration : migrations) {
                    addMigration(migration);
                }
            }
    
            private void addMigration(Migration migration) {
                final int start = migration.startVersion;
                final int end = migration.endVersion;
                SparseArrayCompat<Migration> targetMap = mMigrations.get(start);
                if (targetMap == null) {
                    targetMap = new SparseArrayCompat<>();
                    mMigrations.put(start, targetMap);
                }
                Migration existing = targetMap.get(end);
                if (existing != null) {
                    Log.w(Room.LOG_TAG, "Overriding migration " + existing + " with " + migration);
                }
                targetMap.append(end, migration);
            }
    

    room缓存Migration结构是一个嵌套SparseArrayCompat<SparseArrayCompat<Migration>>,第一层对象startVesion作为key,endVersion的列表作为值。第二层endVersion为key,Migration为值。
    寻找合适的升级Migration逻辑。

    升级:数据库当前版本为key找到对应的SparseArrayCompat<Migration> targetNodes ,从targetNodes最后位置往前寻找,找到符合在当前版本和指定升级版本之间的版本。匹配返回执行Migration逻辑。

    这样做之后我们就不是必须对应上个版本去做改动,我们只要改动两个版本之间的变动就好了,可以直接从1到3,而不必1到2到3.如果按照以前的逻辑Migration(1,2),Migration(2,3)这样添加两个就好了


    总结:

    1. Dao根据具用户定义行为生成Dao_Impl与数据库交互。
    2. RoomDatabase 为Dao提供访问数据库方法
    3. FrameworkSQLiteOpenHelper,SQLiteOpenHelper委托类供RoomDatabase使用
    4. FrameworkSQLiteDatabase,SQLiteDatabase委托类通过SupportSQLiteOpenHelper获取
    5. FrameworkSQLiteStatement,SQLiteStatement委托类

    流程Dao->RoomDatabase->FrameworkSQLiteOpenHelper->SQLiteOpenHelper->FrameworkSQLiteDatabase->SQLiteDatabase->FrameworkSQLiteStatement->SQLiteStatement->SQLiteSession->SQLiteConnectionPool->SQLiteConnection->Native。

    room运行时代码还是比较简单的,烦的应该是compiler中生存代码的逻辑。

    相关文章

      网友评论

          本文标题:SQLite Room

          本文链接:https://www.haomeiwen.com/subject/qcymnqtx.html