黑客攻击的网站,单屏风格wordpress主题,黑龙江外贸网站制作,图片交易网站源码文章目录 SQLite简介创建数据库添加数据查询数据更新数据删除数据升级数据库使用事务参考 SQLite简介
SQLite是一个轻量级关系数据库#xff0c;占用资源很少#xff0c;只有几百KB的大小#xff0c;无需服务器支撑#xff0c;是一个零配置、事务性的SQL数据库引擎。
相对… 文章目录 SQLite简介创建数据库添加数据查询数据更新数据删除数据升级数据库使用事务参考 SQLite简介
SQLite是一个轻量级关系数据库占用资源很少只有几百KB的大小无需服务器支撑是一个零配置、事务性的SQL数据库引擎。
相对于首选项PreferencesSQLite更适合存储大量复杂的关系型数据首选项则适合于保存一些简单的键值对数据比如IM应用的聊天会话信息的本地存储用首选项存储是明显是不合适因为其数据量是极大的数据关系结构也很复杂在这方面首选项是明显是不合适的SQLite则可以很轻松存储操作这些数据。那么SQLite在鸿蒙中是如何使用的下面会一一讲解。
创建数据库
在kit.ArkData方舟数据管理模块中为开发者提供数据存储、数据管理和数据同步能力而SQLite的服务则在这个模块中专门提供了一个relationalStore来辅助创建数据库。我们以一个用户信息为例创建一个名称是user.db的数据库首先创建DBUtils类来管理数据库的行为操作。
import { relationalStore } from kit.ArkData
import AppUtils from ./AppUtils
export default class DBUtils {private static rdbStore?: relationalStore.RdbStoreprivate constructor() {}static init(callback: Function (state: boolean, msg?: string) {}) {const context AppUtils.getContext()// 数据库配置const STORE_CONFIG: relationalStore.StoreConfig {name: user.db, // 数据库名称securityLevel: relationalStore.SecurityLevel.S1, // 数据库安全级别encrypt: false, // 可选参数指定数据库是否加密默认不加密} as relationalStore.StoreConfig// 数据库文件的默认存储路径可通过 customDir修改路径console.log(${TAG} db dir: , context.databaseDir)// 1、获取RdbStore实例用于操作数据库relationalStore.getRdbStore(context, STORE_CONFIG).then((r) {DBUtils.rdbStore rconsole.log(TAG, db create success)callback(true)}).catch((err: Error) {console.error(${TAG} db create error: , err.message)callback(false, err.message)})}
}上面代码是配置和初始化数据库相关的配置主要步骤
创建一个STORE_CONFIG的对象包含了数据库配置的信息有数据库名称、安全级别和加密状态。name是数据库文件名称值是user.db安全级别是relationalStore.SecurityLevel.S1表示数据库的安全级别为低级别当数据泄露时会产生较低影响是不加密的状态。通过relationalStore获取RdbStore实例这是操作数据库的接口通过调用relationalStore.getRdbStore 函数并传入上下文和配置对象来实现。在创建数据库成功后会执行then代码块接着将RdbStore实例赋值给DBUtils.rdbStore这样使得这个实例可以被 DBUtils 类的其他方法使用。最后在外部触发调用DBUitls的init()方法就完成了数据库的创建。当在控制台有打印db create success日志则表示数据库文件创建成功了。
数据库的安全级别除了S1还有S2、S3、S4如下
属性值概述S11表示数据库的安全级别为低级别当数据泄露时会产生较低影响。例如包含壁纸等系统数据的数据库。S22表示数据库的安全级别为中级别当数据泄露时会产生较大影响。例如包含录音、视频等用户生成数据或通话记录等信息的数据库。S33表示数据库的安全级别为高级别当数据泄露时会产生重大影响。例如包含用户运动、健康、位置等信息的数据库。S44表示数据库的安全级别为关键级别当数据泄露时会产生严重影响。例如包含认证凭据、财务数据等信息的数据库。
上面AppUtils是一个简单的工具类用于存储全局context实例代码如下所示
import { common } from kit.AbilityKitexport default class AppUtils {private constructor() {}private static context: common.UIAbilityContextstatic init(context: common.UIAbilityContext) {AppUtils.context context}static getContext(): common.UIAbilityContext {if (!AppUtils.context) {throw new Error(在EntryAbility类的onCreate()方法中调用init()方法完成初始化)}return AppUtils.context}
}通常会在EntryAbility的onCreate()方法中初始化代码如下所示
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {AppUtils.init(this.context)
}如果我们需要查看数据库文件在设备中的位置可以通过context上下文获取数据库文件目录代码如下所示
// 数据库文件的默认存储路径可通过 customDir修改路径
const context AppUtils.getContext()
console.log(${TAG} db dir: , context.databaseDir)应用创建的数据库与其上下文Context有关即使使用同样的数据库名称但不同的应用上下文会产生多个数据库例如每个UIAbility都有各自的上下文。 在控制台我们可以看到打印数据库文件的默认存储路径使用 console.log 来展示数据库目录的路径。如下
DBUtils db dir: /data/storage/el2/database/entry从日志可知数据库文件默认位置是/data/storage/el2/database/entry但在DevEco Studio 5.0.3.400 API12 上发现没有data目录下没有storage目录反而在app目录下可以找到对应的数据库文件完整的文件路径是/data/app/el2/database/entry。 我们可以在DevEco Studio编译器中的Device File Browser工具栏中可以查看到数据库文件。如下图所示 如果希望移动数据库文件到其它地方使用查看则需要同时移动这些以-wal和-shm结尾的临时文件。 在创建数据库文件后此时就要创建数据表来描述数据这里以创建一个USER表为例user表包括了idname、age、sex 、height、weight属性然后通过RdbStore实例的executeSql()方法执行创建数据表的SQL语句代码如下所示 static createTable() {// 创建USER表的SQL语句const CREATE_TABLE_USER CREATE TABLE IF NOT EXISTS USER(ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL, sex INTEGER NOT NULL, height REAL, weight REAL)// 2、创建表DBUtils.rdbStore?.executeSql(CREATE_TABLE_USER).then(() {console.log(TAG, create table done)}).catch((err: BusinessError) {console.error(TAG, err.message)})}
DBUtils.rdbStore对象实例还记得是如何获取的吗在创建数据库文件时通过relationalStore.getRdbStore()来获取的这里把RdbStore实例赋值给了DBUtils类的rdbStore属性。如果数据表创建异常则会执行catch代码块创建成功则执行then代码块。
在完成创建数据USER表后如何可视化查看其内容呢除了将user.db数据库文件导出通过SQLite Studio查看外我们还可以借助于IDE的Database Navigator插件目前在DevEco Studio的Setting - Plugins 是无法搜索到该插件但在其他IDEAndroid Studio、intellij idea是正常能搜索安装的既然在线无法安装我们可以去JetBrains 插件应用市场手动下载后离线安装。
打开网址https://plugins.jetbrains.com/idea 搜索下载 下载后是一个压缩包无需解压然后在DevEco Studio - Settings - Pulgins 安装离线包选择 Install plugin from Disk选项选择下载的压缩包进行安装完成安装后需要重启DevEco Studio才会生效如下图所示 安装成功后会在DevEco Studio的左侧边栏现在应该多出了一个DB Browser工具然后接着回到Device File Browser工具栏打开数据库user.db、user.db-shm和user.db-wal三个文件右击→Save As将它从移动设备导出到你的计算机的任意位置。在DB Browser中选择SQLite如下图所示 最后在弹出窗中选择刚才导出的user.db数据库文件然后点击OK完成配置。 完成配置后在IDE左侧的DB Browser工具栏可以USER表的信息如下图所示 添加数据
对数据库的操作无非就是CRUDC代表添加createR代表查询retrieveU代表更新updateD代表删除delete每个操作都有对应的SQL语句其中添加数据是insert查询数据是select更新数据是update删除数据是delete。
使用RdbStore的insert()方法添加参数第一个参数是表名第二参数是要插入到表中的数据是ValuesBucket对象需要将表中每一列设置对应的值是一个异步方法插入成功则返回的数据在表中的行数插入失败则返回-1代码所下所示
static insert() {let item: relationalStore.ValuesBucket {name: lili,age: 18,sex: 0,height: 160,weight: 45,};let item2: relationalStore.ValuesBucket {name: hzw,age: 28,sex: 1,height: 180,weight: 60,};// 插入数据DBUtils.rdbStore?.insert(DBUtils.tableName, item).then((r) {console.log(TAG, insert success: , r);DBUtils.rdbStore?.commit()}).catch((e: Error) {console.log(TAG, insert err: , e.message);});DBUtils.rdbStore?.insert(DBUtils.tableName, item2).then((r) {console.log(TAG, insert success: , r);DBUtils.rdbStore?.commit()}).catch((e: Error) {console.log(TAG, insert err: , e.message);});}
在上面代码添加了两条数据首先创建ValuesBucket对象对表中的每一列赋值你会发现我们并没有给id赋值这是因为在创建USER表时我们将id设置了自增然后通过DBUtils.rdbStore对象的insert方法用来将数据item插入到名为DBUtils.tableName的表中DBUtils.rdbStore对象是前面已提及到了是rdbStore的实例而DBUtils.tableName的值是USER。
接下来测试添加数据的insert()方法给布局添加插入数据的按钮如下图所示
Entry
Component
struct Index {build() {Column() {Scroll() {Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceEvenly }) {Button(创建数据库).btnStyle(OperateType.CREATE_DB)Button(创建User表).btnStyle(OperateType.CREATE_TABLE)Button(插入数据).btnStyle(OperateType.INSERT)}.width(100%).height(30%)}}.height(100%).width(100%)}
}Extend(Button)
function btnStyle(type: number, call?: (r: string) void) {.margin({ top: 12vp }).onClick(() {switch (type) {case OperateType.CREATE_DB:DBUtils.init()breakcase OperateType.CREATE_TABLE:DBUtils.createTable()breakcase OperateType.INSERT:// 添加数据DBUtils.insert()break}})
}class OperateType {static readonly CREATE_DB: number 0static readonly CREATE_TABLE: number 1static readonly INSERT: number 2static readonly DELETE: number 3static readonly UPDATE: number 4static readonly QUERY: number 5
}上面底代码运行程序后如下图所示 点击“插入数据”按钮会调用 DBUtils.insert()方法将两条数据便会添加到USER数据表中在insert()异步方法中的then代码块中会返回数据在表中行数则表示数据添加成功。另外我们也通过DB Browser查看将user.db等三个数据库文件重新导出到指定目录由于之前已连接数据库重新导出覆盖后点击数据USER表自动刷新重载。如下图所示 从上图可知我们已成功添加了两条数据到USER表中。
查询数据
在添加数据案例中我们是通过DB Browser工具查看数据的在实际开发中通常会通过SQL语句来查询数据在鸿蒙中RdbStore实例提供相关query()查询数据的方法。代码如下所示 static query() {// 创建RdbPredicates实例let predicates new relationalStore.RdbPredicates(DBUtils.tableName);//equalTo 方法的第一个参数是列名第二个参数是列值// 查询namelili的数据predicates.equalTo(name, lili)DBUtils.rdbStore?.query(predicates).then((r) {let items: ArrayrelationalStore.ValuesBucket []// 遍历查询结果while (r.goToNextRow()) {// 获取当前行的数据const row r.getRow()items.push(row)}console.log(TAG, query success: , JSON.stringify(items, null, 2));// 关闭查询结果集r.close()}).catch((e: Error) {console.log(TAG, query err: , e.message);})// 执行SQL语句 查询USER表的所有数据DBUtils.rdbStore?.querySql(SELECT * FROM ${DBUtils.tableName}).then((r) {let items: ArrayrelationalStore.ValuesBucket []// 遍历查询结果while (r.goToNextRow()) {// 获取当前行的数据const row r.getRow()items.push(row)}console.log(TAG, querySql success: , JSON.stringify(items, null, 2));// 关闭查询结果集r.close()}).catch((e: Error) {console.log(TAG, querySql err: , e.message);})}上面代码中query()和querySql()两个不同方法来查询数据query()方法需要接收RdbPredicates实例在RdbPredicates的构造函数设置表名USER通过predicates对象来设置查询的条件equalTo()方法的第一个参数是列名第二个参数是列值则查询name是lili值的数据。在then代码块中通过ResultSet遍历查询每个行的数据,当查询完毕后ResultSet会调用close()方法释放所有的资源。querySql()方法的参数则是SQL语句上面是查询所有的数据then代码块的逻辑与query()方法的then是类似的。
下面我们在布局添加一个“查询数据”的按钮点击按钮时通过DBUtils调用静态query()方法代码如下所示
Button(查询数据).btnStyle(OperateType.QUERY)Extend(Button)
function btnStyle(type: number, call?: (r: string) void) {.margin({ top: 12vp }).onClick(() {switch (type) {case OperateType.CREATE_DB:DBUtils.init()breakcase OperateType.CREATE_TABLE:DBUtils.createTable()breakcase OperateType.INSERT:DBUtils.insert()breakcase OperateType.QUERY:// 查询数据DBUtils.query()break}})
}
运行上面的程序后如下图所示 点击“查询数据”按钮后便会执行查询在控制台会输出查询结果如下所示 查询是一个相对复杂的操作equalTo()方法只是RdbPredicates其中一个系统还提供的其他查询条件的API如下所示 上面的例子只是一个简单的案例在实际开发中要靠自己去慢慢摸索。
更新数据
在学习完添加和查询数据后更新和删除的数据变更就可以通过查询方式来观察变化就不需要将数据库文件导出这样繁琐操作了。RdbStore提供了update()方法来更新数据。代码如下所示 static update() {// 设置更新的列值这里设置了age列的值为25let valueBucket: relationalStore.ValuesBucket {age: 25}// 创建RdbPredicates实例let predicates new relationalStore.RdbPredicates(DBUtils.tableName);// 设置查询的条件name列的值为lili的数据predicates.equalTo(name, lili)// 执行更新操作DBUtils.rdbStore?.update(valueBucket, predicates).then((r: number) {DBUtils.rdbStore?.commit() // 打印更新的行数console.log(TAG, update success: , r)}).catch((e: Error) {console.log(TAG, update err: , e.message)})}上面代码是将lili名称的age值由原来的18改成25接着我们在布局添加一个“更新数据”的按钮点击按钮时通过DBUtils调用静态update()方法代码如下所示
Button(更新数据)
.btnStyle(OperateType.UPDATE)Extend(Button)
function btnStyle(type: number, call?: (r: string) void) {.margin({ top: 12vp }).onClick(() {switch (type) {case OperateType.UPDATE:DBUtils.update()break}})
}运行上面的程序后如下图所示 点击“更新数据”按钮后便会执行更新对应的数据然后点击查询更新后的数据在控制台会输出更新后的结果如下所示 从日志可知在执行update()更新操作后我们更新值是生效了age值由原来的18变成了25.
删除数据
在知道了查询数据后删除数据则相对很简单了RdbStore提供了delete()方法来删除数据只接收一个参数RdbPredicates前面已经使用过多次了已经熟能生巧了就不多讲了直接上代码了。 static delete() {// 创建RdbPredicates实例用于设置查询条件 指定查询的表名let predicates new relationalStore.RdbPredicates(DBUtils.tableName);// 设置删除的条件name列的值为hzw的数据predicates.equalTo(name, hzw)DBUtils.rdbStore?.delete(predicates).then((r: number) {DBUtils.rdbStore?.commit() // 打印删除的行数console.log(TAG, delete success: , r)}).catch((e: Error) {console.log(TAG, delete err: , e.message)})}
上面代码是删除name值为hzw的数据接着我们在布局添加一个“删除数据”的按钮点击按钮时通过DBUtils调用静态delete()方法代码如下所示 Button(删除数据).btnStyle(OperateType.DELETE)
Extend(Button)
function btnStyle(type: number, call?: (r: string) void) {.margin({ top: 12vp }).onClick(() {switch (type) {case OperateType.DELETE:DBUtils.delete()break}})
}运行上面的程序后如下图所示 点击“删除数据”按钮后便会删除对应的数据然后点击查询删除后的数据在控制台会输出删除后的结果如下所示 从日志可知在执行delete()方法执行删除操作后name为hzw的这条数据记录已经被删除了不存在USER表中了。
升级数据库
什么情况下需要升级升级库呢比如我们的应用1.0版本已成功上线了产品在规划2.0版本时用户信息新增一个staffId字段接着在3.0版本时又删除一个weight字段此时数据库就要升级确保在应用版本升级的过程中本地数据库的数据不会丢失。
在初始化数据库配置的getRdbStore()方法的then代码块中进行数据库版本升级。当数据库创建时数据库默认版本是0此时通常会创建需要的表同时将数据库版本设为1相当于从0升级到1代码如下 // 数据库版本号是0时创建数据表语句的SQL语句
const CREATE_TABLE_USER CREATE TABLE IF NOT EXISTS USER(ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL, sex INTEGER NOT NULL, height REAL, weight REAL)relationalStore.getRdbStore(context, STORE_CONFIG).then((store: relationalStore.RdbStore) {DBUtils.rdbStore storeconsole.log(TAG, db ver: ,store.version)// 升级数据库// 当数据库创建时数据库默认版本为0if (store.version 0) {store.executeSql(CREATE_TABLE_USER)// 将版本设置为1 相当于版本号从0升级到1store.version 1}}).catch((err: Error) {console.error(${TAG} db create error: , err.message)
})
此时随着应用版本迭代升级USER表新增了一个staffId字段创建USER表的SQL语句则需要增加一个staffId字段代码如下所示 // 新增staffId字段创建数据表语句的SQL语句
const CREATE_TABLE_USER_1 CREATE TABLE IF NOT EXISTS USER(ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL, sex INTEGER NOT NULL, height REAL, weight REAL, staffId INTEGER)relationalStore.getRdbStore(context, STORE_CONFIG).then((store: relationalStore.RdbStore) {DBUtils.rdbStore storeconsole.log(TAG, db ver: ,store.version)// 升级数据库const oldVersion store.version// 当数据库创建时数据库默认版本为0if (store.version 0) {store.executeSql(CREATE_TABLE_USER_1)// 设置数据库最高版本2 这里始终设置成最高版本号store.version 2}// 如果是数据库版本从1 升级到 2则需要新增staffId字段 if (store.version 1) {store.executeSql(alter table USER add column staffId integer)// 数据库版本升级为2store.version 2 }}).catch((err: Error) {console.error(${TAG} db create error: , err.message)
})
如果是一个新用户初次安装应用数据库默认版本号是0会调用executeSql()方法按照最新的SQL语句(CREATE_TABLE_USER_1)创建USER表同时将数据库版本号设置成最高版本2这样就不会执行后面if升级逻辑如果这个用户的数据库版本是1则会通过executeSql()方法执行alter table USER add column staffId integerSQL语句新增staffId字段同时将数据库版本号升级为2。
当数据库由版本1升级成2时我们去查询数据会发现数据表中有staffId字段了如下
DBUtils query success: [{ID: 1,age: 18,height: 160,name: lili,sex: 0,staffId: null,weight: null
}]接着后面产品又说USER表中不需要weight字段了开发者此时从中表中去除就需要修改创建USER表的语句这样数据库又要升级由版本2升级为3代码如下
// 去除weight字段创建数据表语句的SQL语句
const CREATE_TABLE_USER_2 CREATE TABLE IF NOT EXISTS USER(ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL, sex INTEGER NOT NULL, height REAL, staffId INTEGER)relationalStore.getRdbStore(context, STORE_CONFIG).then((store: relationalStore.RdbStore) {DBUtils.rdbStore storeconsole.log(TAG, db ver: ,store.version)// 升级数据库const oldVersion store.version// 当数据库创建时数据库默认版本为0if (store.version 0) {store.executeSql(CREATE_TABLE_USER_2)// 这里始终设置为最高版本 3store.version 3}// 如果是数据库版本从1 升级到 2则需要新增staffId字段if (store.version 1) {store.executeSql(alter table USER add column staffId integer)store.version 2}// 如果是数据库版本从2 升级到 3则需要去除weight字段if (store.version 2) {store.executeSql(alter table USER drop column weight)store.version 3}}).catch((err: Error) {console.error(${TAG} db create error: , err.message)
})这里的升级逻辑与升级到版本2的逻辑是类似的就不多述了。当版本升级为3时我们去查询数据weight字段则不存在了如下所示
DBUtils query success: [{ID: 1,age: 18,height: 160,name: lili,sex: 0,staffId: null,
}]使用这种if方式来维护数据库的升级不管版本怎样更新都可以保证数据库的表结构是最新的而且表中的数据完全不会丢失。
使用事务
SQLite数据库是支持事务的事务是指一系列操作要么全部完成那么全部不完成是原子性操作。比如我们常用的转账功能A账户向B账户转账可以分为两个步骤从A账户扣钱然后再往B账户打入等量的金额这两个动作是独立的操作可能存在一个成功一个失败比如A账户扣钱成功了B账户没有收到钱出现这种情况是很危险的如何确保两个独立操作要么全部失败要么全部成功当某个失败时就回滚到初始状态此时事务就派上用场了。
在鸿蒙中如何使用事务rdbStore提供了beginTransaction() 和rollBack()方法来保证事务确保操作时原子性。下面以一个简单案例为例代码如下
static async transaction(isError: boolean true){// 开启事务DBUtils.rdbStore?.beginTransaction()try {// 删除hzw的数据let predicates new relationalStore.RdbPredicates(DBUtils.tableName);predicates.equalTo(name, hzw)let rowNum await DBUtils.rdbStore?.delete(predicates)DBUtils.rdbStore?.commit()console.log(TAG, delete success: , rowNum)if (isError) {// 制造一个异常让事务失败throw new Error(error)}let xml: relationalStore.ValuesBucket {name: xml,age: 28,sex: 0,height: 165,};const num await DBUtils.rdbStore?.insert(DBUtils.tableName, xml)DBUtils.rdbStore?.commit()console.log(TAG, insert success: , num)} catch (e) {// 回滚console.log(TAG, 回滚);DBUtils.rdbStore?.rollBack()}
}上面代码的原子性逻辑是先删除hzw的数据然后添加xml名称的数据。
首先在执行SQL前通过beginTransaction()方法开启事务接着删除hzw的数据此时已经执行删除的SQL语句但当isError为true时这人为制造一个异常中断整个流程导致事务的失败但添加数据的操作还未执行。不过由于在catch代码块中调用了rollBack()方法回滚到开启事务处因此hzw数据是删除不了的。
参考
https://blog.csdn.net/K346K346/article/details/114085663