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

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

Android小彩虹2021-12-06 5:38:32140A+A-

本文涉及Library的版本如下:

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

回顾一下安卓的SQLiteOpenHelper相关类

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

android_sqlite

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的类图结构

room_classes

上图有一些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编译时自动生成代码来实现大量模块代码

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示 | 支付宝红包
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们