美文网首页
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中生存代码的逻辑。

相关文章

  • Room

    Save data in a local database using Room Room 提供了一个Sqlite...

  • Android Jetpack - Room

    Room 简介 Room 持久化库提供了一个基于 SQLite 的抽象层,以便在利用 SQLite 的全部功能的同...

  • Android Room 框架学习

    Room是一个对象关系映射(ORM)库。Room抽象了SQLite的使用,可以在充分利用SQLite的同时访...

  • Room数据库的使用方法

    Room 1.简介 官方文档介绍: Room是提供了覆盖在SQLite之上的抽象层,它能在充分利用SQLite全部...

  • Jetpack 之 Room Database 简介及其使用

    简介 Room是SQLite之上的一个抽象层,通过Room,可以更加丝滑的使用SQLite的全部功能。并且,原本在...

  • SQLite Room

    Room 由于SQLiteOpenHelper的接口调用起来比较繁琐。比如插入需要我们自己组装成一个content...

  • Android:ROOM数据库框架

    ROOM ROOM对Sqlite进行了封装,直接使用ROOM来进行数据库访问。 代码:https://github...

  • Room Persistence Library(官网文档翻译)

    官方原文地址 Room持久化库 Room为SQLite提供一个抽象层,在充分利用SQLite的同时,允许流畅的数据...

  • Android 数据存储详解(二):数据库 Room

    数据库 Room Room 在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,...

  • Room数据库框架使用

    概述 room是Google官方推荐的一个数据库Sqlite框架。Room在SQLite上提供了一个抽象层,以便在...

网友评论

      本文标题:SQLite Room

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