前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学习|Android JetPack组件---ORM框架Room的使用

学习|Android JetPack组件---ORM框架Room的使用

作者头像
Vaccae
发布2020-05-14 15:16:42
1.1K0
发布2020-05-14 15:16:42
举报
文章被收录于专栏:微卡智享

导语

Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。

Room简介

Room 包含 3 个主要组件:

  • 数据库:包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点。

使用 @Database 注释的类应满足以下条件:

  1. 是扩展 RoomDatabase 的抽象类。
  2. 在注释中添加与数据库关联的实体列表。
  3. 包含具有 0 个参数且返回使用 @Dao 注释的类的抽象方法。
  4. 在运行时,您可以通过调用 Room.databaseBuilder() 或 Room.inMemoryDatabaseBuilder() 获取 Database 的实例。
  • Entity:表示数据库中的表。
  • DAO:包含用于访问数据库的方法。

应用使用 Room 数据库来获取与该数据库关联的数据访问对象 (DAO)。然后,应用使用每个 DAO 从数据库中获取实体,然后再将对这些实体的所有更改保存回数据库中。最后,应用使用实体来获取和设置与数据库中的表列相对应的值。

Room 不同组件之间的关系图

Room使用

首先把Room的组件在build.gradle中引入进来

代码语言:javascript
复制
    def room_version = "2.2.5"    implementation "androidx.room:room-runtime:$room_version"    implementation "androidx.room:room-ktx:$room_version"

如果用Java的话,上面加下进来应该就可以了,因为我用的是Kotlin,直接就加了上面两个,在编译过程中过不去,查了下原因是Kotlin的配置还要再改一下,并且加入kapt,如下:

代码语言:javascript
复制
    apply plugin: 'kotlin-kapt'
    def room_version = "2.2.5"    kapt "androidx.room:room-compiler:$room_version"    implementation "androidx.room:room-runtime:$room_version"    implementation "androidx.room:room-ktx:$room_version"

01

简单使用

按上面Room的介绍,我们要创建三个主要组件@Database,@Entity和@Dao

类Entity创建

代码语言:javascript
复制
package com.vaccae.roomdemo.bean
import androidx.room.ColumnInfoimport androidx.room.Entityimport androidx.room.PrimaryKey
@Entity(tableName = "Head")class Product {
    @PrimaryKey    @ColumnInfo(name = "Code")    lateinit var code: String
    @ColumnInfo(name = "Name")    lateinit var name: String
    @ColumnInfo(name = "Unit")    lateinit var unit: String
    @ColumnInfo(name = "Price")    var price: Float = 0f}

这里我们创建了一个Product的类,上面@Entity中tableName就是我们在Sqlite数据库中要生成的表名,@PrimaryKey就是设置的code为主键,@ColumnInfo是用于标识表里的列名,如果这个不写的话就默认是你的属性名。

DAO创建

DAO是访问数据库的方法,我们创建了一个接口实现

代码语言:javascript
复制
@Daointerface ProductDao {    @Transaction    @Insert    fun add(vararg arr:Product)    @Transaction    @Delete    fun del(vararg arr:Product)    @Transaction    @Update    fun upd(vararg arr:Product)
    @Query("select * from Head")    fun getAll():List<Product>}

@Insert,@Delelte,@Update和@Query这个会点SQL知识的应该都知道这个增删改查,只有@Query的方法后面要改查询语句,并且返回类型也是自己改的。varary里的arr:Product是可变参数,可以列入多个,当然可以再复写一个List<Product>的方法,直接传入列表也可以。

@Transaction就是开启事务,我把增,删,改都加入了事务。

DataBase创建

代码语言:javascript
复制
@Database(entities = [Product::class], version = 1)abstract class AppDataBase : RoomDatabase() {    abstract fun ProductDao(): ProductDao}

上面的@Database中加入Product的类,version是数据库的版本号,类继承自RoomDataBase,然后在里面把ProductDao的列入即可

调用方法

代码语言:javascript
复制
class DbUtil {
    //创建单例    private var INSTANCE: AppDataBase? = null
    fun getDatabase(context: Context): AppDataBase {        if (INSTANCE == null) {            synchronized(lock = AppDataBase::class) {                if (INSTANCE == null) {                    INSTANCE = Room.databaseBuilder(                        context.applicationContext,                        AppDataBase::class.java, "testdb"                    )                        .allowMainThreadQueries()//允许在主线程查询数据                        .addMigrations()//数据库升级时执行                        .build()                }            }        }        return INSTANCE!!    }}

上面就是创建时实现的单例模式,其中里面的allowMainThreadQueries是允许在主线程查询数据,这个我设置上了,主要是做Demo方便,一般这个不建议加上,后面的addMigrations就是数据库升级时要执行的方法,一会儿后面我会说。

里面的"testdb"就是我们起的数据库名,一会儿创建成功后可以看到

这个DBUtil的类我是和上面的DataBase都建在了一个文件里,因为到时候数据库升级只改这一个就可以了。

上面就把Room基本的设置都已经完成了,然后我们主程序写一个测试方法看看,MainActivity中的代码

代码语言:javascript
复制
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)
        //生成显示产品数据        CreateProduct()
    }
    private fun CreateProduct() {        //加载AppDataBase        val db = DbUtil().getDatabase(this);        for (i in 1..5) {            val item = Product()            item.code = "0000$i"            item.name = "产品$i"            item.unit = "套"            item.price = 99f            //写入数据            db.ProductDao().add(item)        }        //显示出来        val list = db.ProductDao().getAll()        tvshow.text = ""        list.forEach {            tvshow.append(                it.code + " " + it.name                        + " " + it.unit + " " + it.price + "\r\n"            )        }    }}

运行后可以看到,我们创建的5条信息也已经显示了出来,创建成功的数据库在虚拟机下data/data/程序包/database/下的三个文件

打开Sqlite数据库后可以看到了这个表名里的数据

这样,简单的Room就已经实现了。

关于Dao的复用及数据库升级

从上面的创建我们可以看到增、删、改基本都是一样的,所以可以用泛型做一个简单的封装,这样别的类要写Dao时可以继承这个基类不用再改增、删、改了。

BaseDao

代码语言:javascript
复制
package com.vaccae.roomdemo.bean
import androidx.room.*
@Daointerface BaseDao<T> {    @Transaction    @Insert    fun add(vararg arr:T)    @Transaction    @Insert    fun add(arr:ArrayList<T>)
    @Transaction    @Update    fun upd(vararg arr:T)    @Transaction    @Update    fun upd(arr:ArrayList<T>)
    @Transaction    @Delete    fun del(vararg arr:T)    @Transaction    @Delete    fun del(arr:ArrayList<T>)}

关于数据库升级

使用过程中,经常会遇到数据库升级的问题,在Room中使本地SQLITE库数据库升级可以用Migration方式,我们直接做一个新的类,对数据进行操作,也直接在原数据库上升级。

01

新建ProductItem类

代码语言:javascript
复制
package com.vaccae.roomdemo.bean
import androidx.room.ColumnInfoimport androidx.room.Daoimport androidx.room.Entityimport androidx.room.Query

@Entity(tableName = "Body", primaryKeys = ["Code", "BarCode"])class ProductItem {    @ColumnInfo(name = "Code")    lateinit var code: String
    @ColumnInfo(name = "BarCode")    lateinit var barcode: String
    @ColumnInfo(name = "Qty")    var qty = 0}
@Daointerface ProductItemDao : BaseDao<ProductItem> {    @Query("select * from Body")    fun getAll(): List<ProductItem>}

这个类中我把Dao也一起写了进去,直接就是继承自BaseDao,所以只写的Query的查询方法

02

AppDataBase修改

代码语言:javascript
复制
@Database(entities = [Product::class,ProductItem::class], version = 2)abstract class AppDataBase : RoomDatabase() {    abstract fun ProductDao(): ProductDao
    abstract fun ProductItemDao():ProductItemDao}

上面红框中是修改了的部分

03

DBUtil中修改

代码语言:javascript
复制
class DbUtil {
    //数据库升级    var migration1_2 = object : Migration(1, 2) {        override fun migrate(database: SupportSQLiteDatabase) {            val sql="CREATE TABLE if not exists Body(Code TEXT NOT NULL ," +                    "BarCode TEXT NOT NULL,Qty INTEGER NOT NULL,PRIMARY KEY(Code,BarCode))"            database.execSQL(sql)        }    }    //创建单例    private var INSTANCE: AppDataBase? = null
    fun getDatabase(context: Context): AppDataBase {        if (INSTANCE == null) {            synchronized(lock = AppDataBase::class) {                if (INSTANCE == null) {                    INSTANCE = Room.databaseBuilder(                        context.applicationContext,                        AppDataBase::class.java, "testdb"                    )                        .allowMainThreadQueries()//允许在主线程查询数据                        .addMigrations(migration1_2)//数据库升级时执行                        .fallbackToDestructiveMigration()                        .build()                }            }        }        return INSTANCE!!    }}

数据库版本升级要执行的语句我们新建了一个Migration,后面的参数是两个INT,分别是旧的版本号和新的版本号

然后在addMigrtions中加入我们创建的这个Migration,不同版本可以写好几个加入进来,系统会根据当前版本找到对应的方案进行数据库升级

为了防止出现升级失败导致应用程序Crash的情况,我们可以在创建数据库时加入fallbackToDestructiveMigration()方法。该方法能够在出现升级异常时,重新创建数据库表。虽然应用程序不会Crash,但由于数据表被重新创建,所有的数据也将会丢失。

04

MainActivity调用修改

代码语言:javascript
复制
package com.vaccae.roomdemo
import androidx.appcompat.app.AppCompatActivityimport android.os.Bundleimport com.vaccae.roomdemo.bean.*import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)
        //生成显示产品数据        CreateProduct()
        //生成明细数据        CreateProductItem()    }
    private fun CreateProductItem() {        //定义明细列表        val itemlist = ArrayList<ProductItem>()
        //加载AppDataBase        val db = DbUtil().getDatabase(this);        //显示所有Product的明细        val list = db.ProductDao().getAll()
        list.forEach {            for (i in 1..3) {                val item = ProductItem()                item.code = it.code                item.barcode = it.code + i.toString()                item.qty = 1                itemlist.add(item)            }        }        db.ProductItemDao().add(itemlist)
        //显示明细        val getlist= db.ProductItemDao().getAll()        tvshow.text = ""        getlist.forEach {            tvshow.append(                it.code + " " + it.barcode                        + " " + it.qty + "\r\n"            )        }    }
    private fun CreateProduct() {        //加载AppDataBase        val db = DbUtil().getDatabase(this);        for (i in 1..5) {            val item = Product()            item.code = "0000$i"            item.name = "产品$i"            item.unit = "套"            item.price = 99f            //写入数据            db.ProductDao().add(item)        }        //显示出来        val list = db.ProductDao().getAll()        tvshow.text = ""        list.forEach {            tvshow.append(                it.code + " " + it.name                        + " " + it.unit + " " + it.price + "\r\n"            )        }    }}

重新运行后显示的结果如下:

再看数据库中的表也多了对应的数据

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 类Entity创建
  • DAO创建
  • DataBase创建
  • 调用方法
  • 关于Dao的复用及数据库升级
  • 关于数据库升级
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档