android Jetpack Room之数据库的创建

  前面一篇介绍了Room的对于SqliteOpenHelper 的封装,本篇就来了解一下Room 框架中数据库的框架以及对SqliteOpenHelper封装之后 的使用。
  由于Room框架使用的编译时注解,会在编译期间生成很多的类,所以这先贴出一个Room的使用范例

示例

// 定义表非常简单,只需要创建一个 class 并标记上 Entity 注解,
// 可以使用它的 `tableName` 属性声明该表的名称
@Entity(tableName = "table_cache")
class CacheTest {
  // 1. 对于一个表必须存在一个不为空的主键,即必须要标记 PrimaryKey 和 NonNull 两个注解
// PrimaryKey 注解的 `autoGenerate` 属性意味该主键的值,是否由数据库自动生成
    @PrimaryKey(autoGenerate = false)
    @NonNull
    var key: String = ""

    // 2. 该字段在数据库表中的列名称,不指定的默认就等于该字段的名字
    @ColumnInfo(defaultValue = "default value")
    var name: String? = null

    //3. 如果不想让该字段映射成表的列,可以使用该注解标记
    @Ignore
    var timeStamp: Long? = null

}
// 全称(data access object)
@Dao
interface CacheDaoTest { // 1. 如果是插入数据,需要标记上 Insert 注解,并指明插入数据时如果已存在一条主键一样的数据,执行的策略
// REPLACE  : 直接替换老数据
// ABORT    : 终止操作,并回滚事务,即老数据不影响
// IGNORE   : 忽略冲突,但是会插入失败
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun saveCache(cache: CacheTest): Long

    // 2. 常规查询操作,需要写 sql 语句
    @Query("select * from table_cache where `key`=:primaryKey")
    fun getCache(primaryKey: String): CacheTest?

    // 3. 高级查询操作,可以通过 livedata 以观察者的形式获取数据库数据,可以避免不必要的 npe
// 更重要的是其可以监听数据库表中的数据的变化,一旦发生了 insert update delete
// room 会自动读取表中最新的数据,发送给 UI 层刷新页面
    @Query("select * from table_cache")
    fun query(): LiveData<List<CacheTest>>  // 同样支持 rxjava observer

    //4. 删除操作非常简单,也可以执行 sql 语句删除数据
    @Delete(entity = CacheTest::class)
    fun deleteCache(key: String)

    //5. 更新操作,表中对应的这一行所有数据会被替换成 Cache 对象的字段值
    @Update()
    fun update(cache: CacheTest)
}
@Database(entities = [CacheTest::class], version = 3)
abstract class TestDb : RoomDatabase() {
    abstract fun testDao(): CacheDaoTest

    companion object {
        val MIGRATION_2_1: Migration = object : Migration(2, 1) {
            override fun migrate(database: SupportSQLiteDatabase) {
            }
        }

        val MIGRATION_2_3: Migration = object : Migration(2, 3) {
            override fun migrate(database: SupportSQLiteDatabase) {
            }
        }
        val MIGRATION_3_4: Migration = object : Migration(3,4) {
            override fun migrate(database: SupportSQLiteDatabase) {
            }
        }
        val MIGRATION_2_4: Migration = object : Migration(2, 4) {
            override fun migrate(database: SupportSQLiteDatabase) {
            }
        }

    }


}

关于Room的使用不是本文的重点,对于Room的使用不熟的读者可以自行百度。
上面的代码就是一个简单的Room的使用,但是需要这么创建这个数据库呢?

创建数据库

        fun init(context: Context) {
          val  mDataBase = Room.databaseBuilder(
                context.applicationContext,
                TestDb::class.java,
                "user_login_info_db"
            )
                .allowMainThreadQueries()
                .setQueryExecutor(Executors.newSingleThreadExecutor())
                .setTransactionExecutor(Executors.newSingleThreadExecutor())
                .fallbackToDestructiveMigration()
                .createFromAsset("assetFile")
                .addMigrations(TestDb.MIGRATION_2_1)
                .addMigrations(TestDb.MIGRATION_2_3)
                .addMigrations(TestDb.MIGRATION_2_4).addCallback(object : Callback() {
                    override fun onCreate(db: SupportSQLiteDatabase) {
                        super.onCreate(db)
                    }

                    override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
                        super.onDestructiveMigration(db)
                    }

                    override fun onOpen(db: SupportSQLiteDatabase) {
                        super.onOpen(db)
                    }

                })
                .build()
        }
  • allowMainThreadQueries 设置允许在主线程操作数据库
      这里限制的不仅仅是查询操作,如果设置为false,那么增删改查都不可以在主线程执行
  • setQueryExecutor 设置查询操作的线程
  • setTransactionExecutor 执行事务的线程
      上述两个配置只有跟LiveData 结合使用的时候才有效,例如
    fun query(): LiveData<List> 返回值是LiveData 只有用户调用LiveData 的observer方法,此时才会真正开始操作数据库的数据,执行所在的线程就是上面配置的线程。如果返回值不是 LiveData<List>而是List,此时是在那个线程调用query方法就在那个线程执行查询操作,当然如果配置了不允许主线程操作,那么就需要用户自己开启一个新的线程调用query 方法。
      关于这一块,后面会结合着源码解释。
  • fallbackToDestructiveMigration
    当数据库需要升级或者降级的时候可以直接删除旧版本数据库里面的表,然后新建新的表,避免了数据的迁移。
  • createFromAsset
    数据库可能需要预先插入某些数据。
  • addMigrations
    数据库升级或者是降级时候可能需要数据库的迁移,关于这一块请参考上一篇文章,哪里解释的比较清楚。

最后我们进入build 方法看一下

build

        public T build() {
            //noinspection ConstantConditions
            if (mContext == null) {
                throw new IllegalArgumentException("Cannot provide null context for the database.");
            }
            //要创建的数据库
            if (mDatabaseClass == null) {
                throw new IllegalArgumentException("Must provide an abstract class that"
                        + " extends RoomDatabase");
            }
            //事物线程池与数据库操作线程池的设置
            if (mQueryExecutor == null && mTransactionExecutor == null) {
                mQueryExecutor = mTransactionExecutor = ArchTaskExecutor.getIOThreadExecutor();
            } else if (mQueryExecutor != null && mTransactionExecutor == null) {
                mTransactionExecutor = mQueryExecutor;
            } else if (mQueryExecutor == null && mTransactionExecutor != null) {
                mQueryExecutor = mTransactionExecutor;
            }
            

            //检查配置冲突。例如同时配置了冲数据库1升级到数据2的迁移回调,
            //同时配置了 数据库1升级到2的时候直接删除旧的数据表。
            //同时配置两种策略Room框架不知道是用哪一种策略
            if (mMigrationStartAndEndVersions != null && mMigrationsNotRequiredFrom != null) {
                for (Integer version : mMigrationStartAndEndVersions) {
                    if (mMigrationsNotRequiredFrom.contains(version)) {
                        throw new IllegalArgumentException(
                                "Inconsistency detected. A Migration was supplied to "
                                        + "addMigration(Migration... migrations) that has a start "
                                        + "or end version equal to a start version supplied to "
                                        + "fallbackToDestructiveMigrationFrom(int... "
                                        + "startVersions). Start version: "
                                        + version);
                    }
                }
            }
            //SQLiteOpenHelper 的工厂类,
            //一般都是使用FrameworkSQLiteOpenHelperFactory
            if (mFactory == null) {
                mFactory = new FrameworkSQLiteOpenHelperFactory();
            }

            if (mCopyFromAssetPath != null || mCopyFromFile != null) {
                 //mName == null 表示数据库要建立在内存中。
                if (mName == null) {
                    throw new IllegalArgumentException("Cannot create from asset or file for an "
                            + "in-memory database.");
                }
                if (mCopyFromAssetPath != null && mCopyFromFile != null) {
                    throw new IllegalArgumentException("Both createFromAsset() and "
                            + "createFromFile() was called on this Builder but the database can "
                            + "only be created using one of the two configurations.");
                }
                mFactory = new SQLiteCopyOpenHelperFactory(mCopyFromAssetPath, mCopyFromFile,
                        mFactory);
            }
            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;
        }

该注释的都注释了,下面进入RoomDataBase的init方法看看

    public void init(@NonNull DatabaseConfiguration configuration) {
        //创建SQLiteOpenHelper,返回的SQLiteOpenHelper 是在编译期间动态生成的一个
        //因为这个类跟我们定义的数据库里面的表结构息息相关,所以只可以在编译期间
        //生成
        mOpenHelper = createOpenHelper(configuration);
        if (mOpenHelper instanceof SQLiteCopyOpenHelper) {
            SQLiteCopyOpenHelper copyOpenHelper = (SQLiteCopyOpenHelper) mOpenHelper;
            copyOpenHelper.setDatabaseConfiguration(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;
        mQueryExecutor = configuration.queryExecutor;
        mTransactionExecutor = new TransactionExecutor(configuration.transactionExecutor);
        mAllowMainThreadQueries = configuration.allowMainThreadQueries;
        mWriteAheadLoggingEnabled = wal;
        //这个比较有意思,放到后面讲
        if (configuration.multiInstanceInvalidation) {
            mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
                    configuration.name);
        }
    }

createOpenHelper 创建一个RoomOpenHelper,关于这个类在上一篇文章中已经做了介绍

public final class TestDb_Impl extends TestDb {
  private volatile CacheDaoTest _cacheDaoTest;

  @Override
  protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
    final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(3) {
      @Override
      public void createAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("CREATE TABLE IF NOT EXISTS `table_cache` (`key` TEXT NOT NULL, `name` TEXT DEFAULT 'default value', PRIMARY KEY(`key`))");
        _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, '94bf9b88aa2c2b474c4bd436bcde1b56')");
      }

      @Override
      public void dropAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("DROP TABLE IF EXISTS `table_cache`");
        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) {
      	  xxxx
        return new RoomOpenHelper.ValidationResult(true, null);
      }
    }, "94bf9b88aa2c2b474c4bd436bcde1b56", "6ca6e7834faefcdfc4dba1794039f83d");

    //
    final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
        .name(configuration.name)
        .callback(_openCallback)
        .build();
     //sqliteOpenHelperFactory 一般是FrameworkSQLiteOpenHelperFactory,这一点前面
     //介绍了,这里主要是通过工厂模式创建了一个SupportSQLiteOpenHelper 
    final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
    return _helper;
  }
  
  }

TestDb_Impl 是编译期间生成的一个类,前面Room.getGeneratedImplementation 方法通过反射创建了一个实例返回给用户。

public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
    @Override
    public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
        return new FrameworkSQLiteOpenHelper(
                configuration.context, configuration.name, configuration.callback);
    }
}

FrameworkSQLiteOpenHelper 可以看成是一个代理类,实际内容包含在其创建的一个内部类OpenHelper 里面
在这里插入图片描述

OpenHelper 继承了SQLiteOpenHelper , 关于SQLiteOpenHelper 相比大家就都很熟悉了。

在这里插入图片描述

关于SQLiteOpenHelper 与OpenHelper 我们在前面一篇文章已经做了完整的介绍,这里不再赘言。

下一篇文章介绍Room的增删改查,这里会着重介绍Room与LiveData的结合,我觉得这也是Room架构最牛逼的一点。这一点用好了会给我们的开发带来极大的便利。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是关于Android Room数据库的简单介绍和使用方法: Android Room是一种SQLite数据库的对象映射库,它提供了一种简单的方法来访问SQLite数据库,而无需编写复杂的SQL查询。它是Google推出的一种Jetpack组件,旨在帮助Android开发者更加简便地使用SQLite数据库。 下面是使用Android Room数据库的基本步骤: 1. 添加依赖项:在app/build.gradle文件中添加以下依赖项: ``` dependencies { def room_version = "2.3.0" // Room components implementation "androidx.room:room-runtime:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version" // optional - Kotlin Extensions and Coroutines support for Room implementation "androidx.room:room-ktx:$room_version" // optional - RxJava support for Room implementation "androidx.room:room-rxjava2:$room_version" // optional - Guava support for Room, including Optional and ListenableFuture implementation "androidx.room:room-guava:$room_version" // Test helpers testImplementation "androidx.room:room-testing:$room_version" } ``` 2. 创建实体:创建一个Java类或Kotlin数据类,用于表示数据库表中的一行数据。示例代码: ``` @Entity(tableName = "users") data class User( @PrimaryKey val id: Int, @ColumnInfo(name = "user_name") val userName: String, @ColumnInfo(name = "user_age") val userAge: Int ) ``` 3. 创建DAO:创建一个接口,其中包含用于访问数据库的方法。示例代码: ``` @Dao interface UserDao { @Query("SELECT * FROM users") fun getAllUsers(): List<User> @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertUser(user: User) @Delete fun deleteUser(user: User) } ``` 4. 创建数据库创建一个继承自RoomDatabase的抽象类,并在其中声明用于访问DAO的抽象方法。示例代码: ``` @Database(entities = [User::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao } ``` 5. 初始化数据库:在应用程序的入口点处初始化数据库,并使用它来获取DAO实例。示例代码: ``` val db = Room.databaseBuilder( applicationContext, AppDatabase::class.java, "database-name" ).build() val userDao = db.userDao() ``` 6. 使用DAO:使用DAO中定义的方法来访问数据库。示例代码: ``` val allUsers = userDao.getAllUsers() userDao.insertUser(user) userDao.deleteUser(user) ``` 这些是使用Android Room数据库的基本步骤。当然,还有许多其他的高级用法和配置选项,可以根据具体需求进行学习和使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值