Android Jetpack组件之数据库Room详解(二)

栏目: 数据库 · 发布时间: 5年前

内容简介:本文涉及Library的版本如下:首先放一个关于安卓数据库的类图:SQLiteOpenHelper是一个抽象类,通常自己实现数据库,需要继承SQLiteOpenHelper, 在OnCreate()里建表,在onUpgrade()处理版本迁移等。SQLiteOpenHelper是个帮助类,里面SQLiteDatabase类才是真正代表一个数据库。SQLiteDatabase提供了打开数据库,增删改查等方法。我们通常是执行sql语句去操作数据库,只不过官方封装好更方便使用。

本文涉及Library的版本如下:

  • androidx.room:room-runtime:2.1.0-alpha03
  • androidx.room:room-compiler:2.1.0-alpha03(注解编译器)

回顾一下安卓的SQLiteOpenHelper相关类

首先放一个关于安卓数据库的类图:

Android Jetpack组件之数据库Room详解(二)

SQLiteOpenHelper是一个抽象类,通常自己实现数据库,需要继承SQLiteOpenHelper, 在OnCreate()里建表,在onUpgrade()处理版本迁移等。SQLiteOpenHelper是个帮助类,里面SQLiteDatabase类才是真正代表一个数据库。SQLiteDatabase提供了打开数据库,增删改查等方法。我们通常是执行 sql 语句去操作数据库,只不过官方封装好更方便使用。

上图中SQLiteProgram是抽象类,是编译 SQLite 程序的基类。成员变量里有sql语句、表字段名数据,相对应的字段的值。SQLiteStatement继承SQLiteProgram, 提供一下执行语句的方法。SQLiteQurey也是继承SQLiteProgram,代表了查询的执行操作。安卓的数据库操作把查询操作和其他操作分开。通过SQLiteDirectCursorDriver驱动执行SQLiteQurey生成SQLiteCursor游标来去数据; 建表,删表,建索引等是通过SQLiteStatement.excute()执行; 更新和删除通过SQLiteStatement.executeUpdateDelete()执行; 插入数据通过SQLiteStatement.executeInsert()。Room在原有的基础上进行了封装。

Room的类图结构

Android Jetpack组件之数据库Room详解(二)

上图有一些Support开头的接口, 这些接口存在androidx.sqlite:sqlite:2.0.0库里, 这个是对安卓原有数据库操作做了接口的抽离。SupportSQLiteDatabase对应SQLiteDatabase,、SupportSQLiteOpenHelper对应SQLiteOpenHelper、SupportSQLiteProgram对应SQLiteProgram等等;

Framework开头一些类的是对一些Support接口的实现;Framework开头这些类存在于androidx.sqlite:sqlite-framework:2.0.0库中, FrameworkSQLiteDatabase实现里有个成员变量SQLiteDatabase,实现的接口都是交给SQLiteDatabase处理。FrameworkSQLiteOpenHelper、FrameworkSQLiteProgram、FrameworkSQLiteStatement都是这个套路,使用装饰者模式,真正的实现还是安卓原有数据库操作的类。FrameworkSQLiteOpenHelperFactory工厂返回得是FrameworkSQLiteOpenHelper.OpenHelper类,FrameworkSQLiteOpenHelper.OpenHelper继承SQLiteOpenHelper。

Room开头这些类存在androidx.room:room-runtime:2.10库中, 这个库基于Support这类接口(例如:SupportSQLiteDatabase)和Framework实现(FrameworkSQLiteDatabase的实现)再次封装。room使用过程中需要定义一个抽象AppDatabase继承RoomDatabase,使用Room.databaseBuilder()去生成AppDatabase的实现。

Room数据库表的创建

AppDatabase是一个抽象类,真正的实现是AppDatabase_Impl, AppDatabase_Impl是用编译时注解生成的,注解编译器是androidx.room:room-compiler:$room_version。AppDatabase实现类是由RoomDatabase.Builder.build()创建的,先看build方法的实现:

public T build() {
    ......
        if (mFactory == null) { // SQLiteOpenHelper工厂
            mFactory = new FrameworkSQLiteOpenHelperFactory();
        }
    //DatabaseConfiguration是数据配置类,
    //存储context, 数据库名,SQLiteOpenHelperFactory等参数
    DatabaseConfiguration configuration =
        new DatabaseConfiguration(mContext, mName, mFactory, mMigrationContainer,
                                  mCallbacks, mAllowMainThreadQueries, mJournalMode.resolve(mContext),
                                  mQueryExecutor,
                                  mMultiInstanceInvalidation,
                                  mRequireMigration,
                                  mAllowDestructiveMigrationOnDowngrade, mMigrationsNotRequiredFrom);
    //DB_IMPL_SUFFIX = "_Impl", db的实现是AppDatabase_Impl
    T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
    //init方法实现在RoomDatabase
    db.init(configuration);
    return db;
}

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;
	// klass的全包名 + "_Impl",反射调用newInstance()生成实例
    final Class<T> aClass = (Class<T>) Class.forName(
        fullPackage.isEmpty() ? implName : fullPackage + "." + implName);
    return aClass.newInstance();
}

//RoomDatabase.init方法
public void init(@NonNull DatabaseConfiguration configuration) {
    //建表
    mOpenHelper = createOpenHelper(configuration);
    boolean wal = false;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        wal = configuration.journalMode == JournalMode.WRITE_AHEAD_LOGGING;
        //是否打开日志
        mOpenHelper.setWriteAheadLoggingEnabled(wal);
    }
    mCallbacks = configuration.callbacks;
    // 查询Executor:决定查询执行的线程
    mQueryExecutor = configuration.queryExecutor; 
    mAllowMainThreadQueries = configuration.allowMainThreadQueries;
    mWriteAheadLoggingEnabled = wal;
    if (configuration.multiInstanceInvalidation) {
        mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
                                                            configuration.name);
    }
}

复制代码

RoomDatabase.createOpenHelper方法是抽象方法,实现在AppDatabase_Impl, 定位到AppDatabase_Impl.createOpenHelper方法

@Override
  protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
      //_openCallback是局部匿名内部实例, 是一个RoomOpenHelper实例, 先不管这个实例,接着看下面。
    final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
      @Override
      public void createAllTables(SupportSQLiteDatabase _db) {
          //执行建表语句
        _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`first_name` TEXT, `name` TEXT, `id` INTEGER NOT NULL, PRIMARY KEY(`id`))");
      }

      @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);
          }
        }
      }

    }, "e216f2ddb0b894667088e1e7fec58cdd", "07bca20d2ba295fc9d4acbe7a3f64d4b");
      
      //SupportSQLiteOpenHelper.Configuration数据库相关配置
      //存储了context, name(数据库名字),SupportSQLiteOpenHelper.Callback对象
    final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
        .name(configuration.name)
        .callback(_openCallback)
        .build();
      //工厂生成SupportSQLiteOpenHelper, 这里的工厂默认是FrameworkSQLiteOpenHelperFactory
      //默认值在RoomDatabase.build()里赋值
    final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
    return _helper;
  }

//FrameworkSQLiteOpenHelperFactory类的实现
public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
    @Override
    public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
        //new 一个FrameworkSQLiteOpenHelper, 接着看FrameworkSQLiteOpenHelper的构造器
        //获取SupportSQLiteOpenHelper.Configuration的context, name, callback
        return new FrameworkSQLiteOpenHelper(
                configuration.context, configuration.name, configuration.callback);
    }
}

//FrameworkSQLiteOpenHelper的构造器
FrameworkSQLiteOpenHelper(Context context, String name,
                          Callback callback) {
    mDelegate = createDelegate(context, name, callback);
}
//FrameworkSQLiteOpenHelper.createDelegate方法
private OpenHelper createDelegate(Context context, String name, Callback callback) {
    final FrameworkSQLiteDatabase[] dbRef = new FrameworkSQLiteDatabase[1];
    //OpenHelper是FrameworkSQLiteOpenHelper的内部类
    return new OpenHelper(context, name, dbRef, callback);
}

//OpenHelper继承安卓的SQLiteOpenHelper
static class OpenHelper extends SQLiteOpenHelper {
    final FrameworkSQLiteDatabase[] mDbRef;
    final Callback mCallback;
    // see b/78359448
    private boolean mMigrated;

    OpenHelper(Context context, String name, final FrameworkSQLiteDatabase[] dbRef,
               final Callback callback) {
        super(context, name, null, callback.version,
              new DatabaseErrorHandler() {
                  @Override
                  public void onCorruption(SQLiteDatabase dbObj) {
                      FrameworkSQLiteDatabase db = dbRef[0];
                      if (db != null) {
                          callback.onCorruption(db);
                      }
                  }
              });
        mCallback = callback;
        mDbRef = dbRef;
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        //数据库初始化, mCallback是RoomOpenHelper
        mCallback.onCreate(getWrappedDb(sqLiteDatabase));
    }
}   

//RoomOpenHelper.onCreate方法
@Override
public void onCreate(SupportSQLiteDatabase db) {
    //更新Identity(这里跳过,不深入)
    updateIdentity(db);
    //mDelegate是RoomOpenHelper的内部类Delegate, Delegate是一个抽象类,
    //定义了createAllTables, onCreate, onOpen等方法
    //mDelegate在这里实现是AppDatabase_Impl.createOpenHelper方法里_openCallback实例
    
    //调用建表方法
    mDelegate.createAllTables(db);
    //调用onCreate方法
    mDelegate.onCreate(db);
    
    //回看_openCallback实例的实现, createAllTables里执行了建表语句
}

复制代码

Room数据库插入操作过程

Room数据访问只需要定义一个DAO接口, 在Dao接口里定义方法以及注解即可。

@Dao
public interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(User user);
}

//调用
AppDatabase.getInstance().userDao().insert(user);
复制代码

前面有说到AppDatabase实现类AppDatabase_Impl, 而这里UserDao的实现类是UserDao_Impl。UserDao_Impl也是编译时自动生成实现类,先看一下AppDatabase_Impl.userDao()

@Override
  public UserDao userDao() {
    if (_userDao != null) {
      return _userDao;
    } else {
      synchronized(this) {
        if(_userDao == null) {
          _userDao = new UserDao_Impl(this);
        }
        return _userDao;
      }
    }
  }
复制代码

接口UserDao_Impl的实现

//UserDao_Impl构造器
public UserDao_Impl(RoomDatabase __db) {
    this.__db = __db;
    // 创建一个EntityInsertionAdapter对象
    this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {
        
      //插入的数据的sql语句
      @Override
      public String createQuery() {
        return "INSERT OR ABORT INTO `User`(`first_name`,`name`,`id`) VALUES (?,?,?)";
      }

      //绑定插入的字段,对应上面sql语句的‘?’占位符
      @Override
      public void bind(SupportSQLiteStatement stmt, User value) {
        if (value.firstName == null) {
          stmt.bindNull(1); // 对应第一个“?”占位符,代表更新first_name的值
        } else {
          stmt.bindString(1, value.firstName);
        }
        if (value.name == null) {
          stmt.bindNull(2);
        } else {
          stmt.bindString(2, value.name);
        }
        stmt.bindLong(3, value.id);
      }
    };
}    

@Override
  public void insert(final User user) {
    __db.beginTransaction();
    try { // 开启事务,进行插入数据
      __insertionAdapterOfUser.insert(user);
      __db.setTransactionSuccessful();
    } finally {
      __db.endTransaction();
    }
  }
复制代码

从上面的代码可以知道,最后开启一个事务,调用EntityInsertionAdapter.insert方法进行数据插入, EntityInsertionAdapter是一个抽象类,利用泛型封装了支持所有类型的数据插入,实现类必须要实现createQuery和bind两个方法。createQuery方法返回一条sql语句,而bind方法是对数据类进行绑定,和sql语句是一一对应的。

EntityInsertionAdapter.insert方法如下:

public final void insert(T entity) {
        final SupportSQLiteStatement stmt = acquire(); //创建一个SupportSQLiteStatement, 这里会调用到createQuery方法
        try {
            bind(stmt, entity);//会调用bind(SupportSQLiteStatement stmt, T value)
            stmt.executeInsert();//执行sql语句
        } finally {
            release(stmt);
        }
    }
复制代码

最终数据类的插入,通过SupportSQLiteStatement接口去执行sql语句。看回本篇文章开头的类结构图,SupportSQLiteStatement是一个接口, 实现是FrameworkSQLiteStatement, 而FrameworkSQLiteStatement内部实现是有一个委派者SQLiteStatement去执行的,最终也是调用安卓原有SQLiteStatement类去执行。

Room数据库查询过程

查询也类似,通过定义一个Dao接口和@Query注解,大致代码如下:

@Dao
public interface UserDao {
    @Query("SELECT * FROM user WHERE id = :id")
    User findById(String id);
}    
复制代码

UserDao_Impl.findById实现如下:

@Override
  public User findById(final String id) {
    final String _sql = "SELECT * FROM user WHERE id = ?";
    //通过sql创建SQLite查询执行程序
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
    int _argIndex = 1;
    if (id == null) {
      _statement.bindNull(_argIndex);
    } else {
      _statement.bindString(_argIndex, id);//绑定参数id
    }
    final Cursor _cursor = DBUtil.query(__db, _statement, false);//执行查询语句
      //下面通过cursor获取数据并赋值给User
    try {
      final int _cursorIndexOfFirstName = CursorUtil.getColumnIndexOrThrow(_cursor, "first_name");
      final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
      final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
      final User _result;
      if(_cursor.moveToFirst()) {
        _result = new User();
        _result.firstName = _cursor.getString(_cursorIndexOfFirstName);
        _result.name = _cursor.getString(_cursorIndexOfName);
        _result.id = _cursor.getInt(_cursorIndexOfId);
      } else {
        _result = null;
      }
      return _result;
    } finally {
      _cursor.close();
      _statement.release();
    }
  }
复制代码

总结

  • Room对安卓原有的数据类相关类进行一次封装,对SQLiteOpenHelper, SQLiteDatabase, SQLiteProgram,SQLiteStatement,SQLiteQurey抽象出了对应的接口SupportSQLiteOpenHelper, SupportSQLiteDatabase, SupportSQLiteProgram, SupportSQLiteStatement, SupportSQLiteQurey。然后提供了一套Framework字母开头的系列类是对Support接口的实现。
  • 抽象出一系列Support接口,可以给开发者去实现一套不是基于sqlite数据库。例如可以替代sqlite的开源库realm
  • Room是一个基于DAO架构数据库框架,利用apt编译时自动生成代码来实现大量模块代码

以上所述就是小编给大家介绍的《Android Jetpack组件之数据库Room详解(二)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Powerful

Powerful

Patty McCord / Missionday / 2018-1-25

Named by The Washington Post as one of the 11 Leadership Books to Read in 2018 When it comes to recruiting, motivating, and creating great teams, Patty McCord says most companies have it all wrong. Mc......一起来看看 《Powerful》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具