一文带你了解Room数据库

1.引言

Sqlite的代码写起来繁琐且容易出错,数据和对象之间的映射过程写起来实在无趣,还是不如使用ORM框架来的得心应手。

2.Room是什么

Room是Goole推出的数据库框架,属于ORM库。

Room提供了SQLite的抽象层,以便在充分利用SQLite的同时允许流畅的数据库访问。

Room与其他ORM框架对比有以下优点:

  1. 编译期检查,Room会在编译的时候验证每个@Query@Entity等,它不仅检查语法问题,还会检查是否有该表,这就意味着几乎没有任何运行时错误的风险
  2. 较少的模板代码
  3. 与 LiveData集成

Room有3个主要组件:

  • Entity : 数据实体,对应数据库中的表
  • DAO:数据访问对象,包含访问数据库的方法
  • Database:数据库持有者。

Room的架构图:
在这里插入图片描述

3.Room使用

在project的build.gradle中加入google的Maven仓库(高版本的AS自动添加):


allprojects {
    repositories {
        google()
    }
}

在app的build.gradle中添加依赖:

apply plugin: 'kotlin-kapt'
dependencies {
    def room_version = "2.2.5"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
  }
    

至此,Room就已经导入进来了。

1.Entity(创建表)
@Entity(tableName = "Book")
class Book{
    @PrimaryKey()
    var id :Int = 0
    @ColumnInfo(name = "book_name")
    var bookName:String? = null
    var isbn:String? = null
    var anchor:String? = null
    @Ignore
    var price :Int = 0

    override fun toString(): String {
        return "Book(id=$id, bookName=$bookName, isbn=$isbn, anchor=$anchor, price=$price)"
    }

}

创建表一般会用到下面几个注解:

@Entity(tableName=“表名称”)

定义一个表

@PrimaryKey(autoGenerate=true)

定义主键

@ColumnInfo(name = “列名”)

定义列名

@Ignore

忽略某个字段

2.Dao

@Dao
abstract class BookDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract fun insert(book: Book)

    @Delete
    abstract fun delete(book:Book)

    @Update
    abstract fun update(book:Book)

    @Query("select * from Book where id =:id")
    abstract fun queryById(id:Int) : Book

    @Query("select * from Book")
    abstract fun queryAll():List<Book>

}

@Insert@Delete@Update@Query 代表我们常用的插入、删除、更新、查询数据库操作。

Dao非常的灵活,你可以传入不同的参数,像这样:

    @Insert
    abstract fun insert(books:List<Book>)

    @Delete
    abstract fun delete(vararg books: Book)

当然,你也可以返回不同的类型,例如:

   @Query("select book_name from Book")
    abstract fun queryAllBookName() : List<String?>

    @Query("select count(*) from Book")
    abstract fun bookCount() : Int

@Query非常的强大,你可以编写任意sql语句并得到你想要的结果。

3.DataBase

@Database(entities = [Book::class],version = 1)
abstract class AppDatabase : RoomDatabase(){

    abstract fun bookDao(): BookDao

}

创建好这三个组件,Room的配置基本就已经完成了,通常我们需要的业务逻辑都加写在Dao中的抽象方法中。

点击Build编译项目,Room会自动生成对应的实现类。验证上面的代码:

class MainActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val appDb = Room.databaseBuilder(this, AppDatabase::class.java, "myDb.db")
            .allowMainThreadQueries()
            .build()
        val bookDao = appDb.bookDao()

        val ludingji = Book()
        ludingji.id = 1
        ludingji.bookName = "《鹿鼎记》"

        val tianlongbabu = Book()
        tianlongbabu.id = 2
        tianlongbabu.bookName = "《天龙八部》"

        bookDao.insert(ludingji)
        bookDao.insert(tianlongbabu)
        Log.d("MainActivity",bookDao.queryAll().toString())
        Log.d("MainActivity",bookDao.queryById(1).toString())
        bookDao.delete(ludingji)
        Log.d("MainActivity",bookDao.queryAll().toString())
        tianlongbabu.anchor = "金庸"
        bookDao.update(tianlongbabu)
        Log.d("MainActivity",bookDao.queryById(2).toString())

    }
        
        
    //输出结果:
    //[Book(id=1, bookName=《鹿鼎记》, isbn=null, anchor=null, price=0), Book(id=2, bookName=《天龙八部》, isbn=null, anchor=null, price=0)]
    //[Book(id=2, bookName=《天龙八部》, isbn=null, anchor=null, price=0)]
    //Book(id=1, bookName=《鹿鼎记》, isbn=null, anchor=null, price=0)
    //Book(id=2, bookName=《天龙八部》, isbn=null, anchor=金庸, price=0)
}

4.数据库迁移

在Room中,数据库迁移使用的是Migration对象,定义如下:

  public abstract class Migration {
    public final int startVersion;
    public final int endVersion;

    public Migration(int startVersion, int endVersion) {
        this.startVersion = startVersion;
        this.endVersion = endVersion;
    }

    public abstract void migrate(@NonNull SupportSQLiteDatabase database);
}

startVersion是旧版本号,endVersion是新版本号。数据库版本发生变更(如升级)会回调migrate函数,我们需要在此回调中编写版本变更的相关代码,例如创建表、添加列等等。

把Migration添加到对应的db中:

 val appDb = Room.databaseBuilder(this, AppDatabase::class.java, "myDb.db")
            .allowMainThreadQueries()
            .addMigrations(object : Migration(1,2){
                override fun migrate(database: SupportSQLiteDatabase) {
                  //升级相关操作
                }
            })
            .build()

5.数据库记录

Room提供了记录每个版本数据库信息的方式:

    defaultConfig {
        //...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
            }
        }
    }

编译后,会在对应路径生成schemas文件夹:
在这里插入图片描述

1.json中的1表示版本号,json包含了各个版本的概要,表结构等信息:

{
  "formatVersion": 1,
  "database": {
    "version": 1,
    "identityHash": "5d16d39b710ef34dd63824a76d120e02",
    "entities": [
      {
        "tableName": "Book",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `book_name` TEXT, `isbn` TEXT, `anchor` TEXT, PRIMARY KEY(`id`))",
        "fields": [
          {
            "fieldPath": "id",
            "columnName": "id",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "bookName",
            "columnName": "book_name",
            "affinity": "TEXT",
            "notNull": false
          },
          {
            "fieldPath": "isbn",
            "columnName": "isbn",
            "affinity": "TEXT",
            "notNull": false
          },
          {
            "fieldPath": "anchor",
            "columnName": "anchor",
            "affinity": "TEXT",
            "notNull": false
          }
        ],
        "primaryKey": {
          "columnNames": [
            "id"
          ],
          "autoGenerate": false
        },
        "indices": [],
        "foreignKeys": []
      }
    ],
    "views": [],
    "setupQueries": [
      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5d16d39b710ef34dd63824a76d120e02')"
    ]
  }
}

3.总结

文章描述了Room的基本用法,更多请参考官方文档

更多安卓知识体系,请关注公众号:
三分钟Code


版权声明:本文为weixin_38261570原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。