当前位置:   article > 正文

《第一行代码》kotlin Android SQLite

《第一行代码》kotlin Android SQLite

参考 《第一行代码 第三版》教程
Android 内置了SQLite数据库

SQLite 是一款轻量级的关系型数据库,运算速度快,占用资源少,支持标准SQL语法,数据库ACID事务,可以用于记录复杂的关系性数据

  1. 创建数据库
    Android提供了SQLiteOpenHelper帮助类

SQLiteOpenHelper是一个抽象类,需要继承它,实现两个抽象方法 onCreate()onUpgrade(), 在这两个方法创建和升级数据库

SQLiteOpenHelper 具有两个重要的实例方法: getReadableDatabase()getWritableDatabase(), 两个方法都可以创建或打开现有数据库(如果数据库已存在则直接打开,否则要创建一个新的数据库),并返回读写操作对象。当数据库不可写(磁盘已满)时, getReadableDatabase()以只读方式打开数据库, getWritbaleDatabase() 出现异常

SQLiteOpenHelper 有两个构造方法可供重写, 一般重写参数少的那个即可

接受4个参数:
Context: 操作数据库
数据库名: 创建数据库时的名称
Cursor: 运行查询数据库是返回一个Cursor,一般传入null
当前数据库版本号:用于数据库升级

构建SQLiteOpenHelper之后,调用getReadableDatabase() 或 getWritableDatabase() 后创建数据库

存放目录: /data/data/<package name>/databases/

案例: 新建Book表, 有id(主键)、作者、价格、页数、书名列
建表语句:

create table Book ( 
 id integer primary key autoincrement, 
 author text, 
 price real, 
 pages integer, 
 name text)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

integer表示整型,real表示浮点型,text表示文本类型,blob表示二进制类型。
primary key 主键
autoincrement 自增长

建表代码:

class MyDatabaseHelper(val context: Context, name: String, version: Int) :
    SQLiteOpenHelper(context, name, null, version) {
    private val createBook = "create table Book (" + 
 " id integer primary key autoincrement," + 
 "author text," + 
 "price real," + 
 "pages integer," + 
 "name text)"

    override fun onCreate(db: SQLiteDatabase) {
		db.execSQL(createBook) // 执行SQL
		Toast.makeText(context, "Success", Toast.LENGTH_SHORT).show()
    }
    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

新建一个createDatabase 按钮
在onCreate() 方法中构建一个MyDatabaseHelper对象, 点击时调用getWritableDatabase() 方法

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceStanceState)
  ...
  val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
  createDatbase.setOnClickListener {
    dbHelper.writeableDatabase
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

点击后会创建数据,再次点击不会重新创建一次
/data/data/com.e xample.databasetest/databases/ 下的数据库保存到电脑中
然后使用Database Navigator 插件来查看数据库,
在这里插入图片描述
双击Book表,然后点击no filter 查看表内容

  1. 升级数据库
    MyDatabaseHelper 的 onUpgrade() 方法用于升级数据库
    比如新增Category 表用于记录图书分类
private val createCategory = "create table Category (" + 
 "id integer primary key autoincrement," + 
 "category_name text," + 
 "category_code integer)" 
 override fun onCreate(db: SQLiteDatabase) { 
 db.execSQL(createBook) 
 db.execSQL(createCategory) 
 Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

但是没有新建成功,

onUpgrade 中执行drop语句:

override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
 db.execSQL("drop table if exists Book") 
 db.execSQL("drop table if exists Category") 
 onCreate(db) 
 }
  • 1
  • 2
  • 3
  • 4
  • 5

如何执行 onUpgrade() 方法?
需要传入版本号,要比之前的大

val dbHelper = MyDatabaseHelper(this, "BookStore.db", 2)
createDatabase.setOnClickListener {
  dbHelper.writableDatabase
}
  • 1
  • 2
  • 3
  • 4

CRUD(增查改删) : create添加 retrieve查询 update更新 delete删除

getReadableDatabase 和 getWritableDatabase 用于创建、升级数据库, 会返回一个SQLiteDatabase对象, 借助这个对象CRUD

  1. 添加数据
    insert方法,接受3个参数
    第一个:表名,
    第二个:用于在未指定添加数据时,给某些可为空的列赋值NULL, 一般用不到,传入null
    第三个:一个ContentValues对象, 提供了一系列put方法重载,用于向ContentValus添加数据,只需要将列名及相应待添加数据传入即可

实例,新建addDate按钮:

addData.setOnClickLisener {
  val db = dbHelper.writeableDatabase
  val values1 = ContentValues().apply {
    put("name", "first line code")
    put("author", "guolin")
    put("pages", 100)
    put("price", 16)
  }
  db.insert("Book", null, values1)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  1. 更新数据
    update方法,接受4个参数
    第一个:表名,
    第二个:是ContentValues对象,要把更新数据在这里组装进去
    第三个:第三、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认会更新所有行

更新书本价格:

updateData.setOnClickListener {
  val db = dbHelper.writeableDatabase
  val values = ContentValues()
  values.put("price", 10)
  db.update("Book", values, "name=?", arrayOf("first line code"))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. 删除数据
    delete(),接受3个参数
    第一个:表名,
    第二、第三个参数用于约束删除某一行或某几行的数据,不指定的话默认会删除所有行。
daleteData.setOnClickListener {
  val db = dbHelper.writableDatabase
  db.delete("Book", "pages > ?", arrayOf("500"))
}
  • 1
  • 2
  • 3
  • 4
  1. 查询数据
    query(), 最短的方法也要传入7个参数
    第一个 表名

第二个 参数用于指定去查询哪几列,如果不指定则默认查询所有列。
第三、第四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行的数据。第五个参数用于指定需要去group by 的列,不指定则表示不对查询结果进行group by 操作。
第六个参数用于对group by 之后的数据进行进一步的过滤,不指定则表示不进行过滤。
第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。

调用query() 方法后会返回一个Cursor对象, 查询到的数据将从这个对象取出

queryData.setOnClickListener {
  val db = dbHelper.writableDatabase
  // 查询Book表中所有的数据
  val cursor = db.query("Book", null, null, null, null, null, null)
  if (cursor.moveToFirst()) {
      do {
          // 遍历Cursor对象,取出数据并打印
          val name = cursor.getString(cursor.getColumnIndexOrThrow("name"))
          val author = cursor.getString(cursor.getColumnIndexOrThrow("author"))
          val pages = cursor.getInt(cursor.getColumnIndexOrThrow("pages"))
          val price = cursor.getDouble(cursor.getColumnIndexOrThrow("price"))
          Log.d("MainActivity", "book name is $name")
          Log.d("MainActivity", "book author is $author")
          Log.d("MainActivity", "book pages is $pages")
          Log.d("MainActivity", "book price is $price")
      } while (cursor.moveToNext())
  }
  cursor.close()
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

调用query方法后,返回一个Cursor对象
调用moveToFirst(), 将指针移到第一行, 进入一个循环,遍历查询每行数据
通过Cursor的getColumnIndexOrThrow() 方法获取某一列在表中对应的位置索引,然后使用 cursor.getString 方法取出值
最后使用close方法来关闭Cursor()

  1. 直接执行SQL语句查询
    (1)添加数据:
    db.execSQL(“insert into Book (name, author, pages, price) values(?,?,?,?)”, arrayOf(“fist line code”, “guolin”, “100”, “66”))
    (2)更新数据
    db.execSQL(“update Book set price = ? where name = ?”, arrayOf(“10.99”, “The Da Vinci Code”))
    (3)删除数据
    db.execSQL(“delete from Book where pages > ?”, arrayOf(“500”))
    (4)查询数据
    val cursor = db.rawQuery(“select * from Book”, null)
    查询使用rawQuery()方法,
    其他操作都是调用的execSQL()方法。

  2. 使用事务
    保证多个操作一起成功或者失败

添加 replaceData 按钮

replaceData.setOnClickListener{
            val db = dbHelper.writableDatabase
            db.beginTransaction() // 开启事务
            try {
                db.delete("Book", null, null)
                if (true) {
//                    手动抛出,让事务失败
                    throw NullPointerException()
                }
                val values = ContentValues().apply {
                    put("name", "Game of Thrones")
                    put("author", "George Martin")
                    put("pages", 720)
                    put("price", 20.85)
                }
                db.insert("Book", null, values)
                db.setTransactionSuccessful() // 事务已经执行成功
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                db.endTransaction()
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

beginTransaction()方法开启一个事务
当所有的操作都完成之后,调用setTransactionSuccessful()表示事务已经执行成
功了
最后在finally代码块中调用endTransaction()结束事务

中途出现异常会导致事务的失败,此时旧数据应该是删除不掉的

  1. 升级数据库最佳写法
    原来的方法升级后数据会全部失去。 如何保证数据不会丢失?
    判断对应的版本,然后在onUpgrade 中直接执行建表或更新列语句

每当升级一个数据库版本的时候,onUpgrade()方法里都一定要写一个相应的if判断语句。 保证跨版本升级数据库修改能被全部执行

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/1009205
推荐阅读
相关标签
  

闽ICP备14008679号