第五章:HarmonyOS数据存储
5.1、关系型数据库概述
关系型数据库(Relational Database,RDB)是一种基于关系模型来管理数据的数据库。HarmonyOS关系型数据库基于SQLite组件提供了一套完整的对本地数据库进行管理的机制,对外提供了一系列的增、删、改、查等接口,也可以直接运行用户输入的SQL语句来满足复杂的场景需要。HarmonyOS提供的关系型数据库功能更加完善,查询效率更加高效。
基本概念
-
关系型数据库
基于关系模型来管理数据的数据库,以行和列的形式存储数据。
-
谓词
数据库中用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数据库的操作条件。
-
结果集
指用户查询之后的结果集合,可以对数据进行访问。结果集提供了灵活的数据访问方式,可以更方便的拿到用户想要的数据。
-
SQLite数据库
一款轻型的数据库,是遵守ACID的关系型数据库管理系统。它是一个开源的项目。
运作机制
HarmonyOS关系型数据库对外提供通用的操作接口,底层使用SQLite作为持久化存储引擎,支持SQLite具有的所有数据库特性,包括但不限于事务、索引、视图、触发器、外键、参数化查询和预编译SQL语句。
图1 关系型数据库运作机制
5.2、数据库接口说明
数据库的创建和删除
关系型数据库提供了数据库创建方式,以及对应的删除接口,涉及的API如下所示。
类名 | 接口名 | 描述 |
---|---|---|
DatabaseHelper | DatabaseHelper(Context context) | DatabaseHelper是数据库操作的辅助类,当数据库创建成功后,数据库文件将存储在由上下文指定的目录里。数据库文件存储的路径会因指定不同的上下文存在差异。获取上下文参考方法:ohos.app.Context#getApplicationContext()、ohos.app.AbilityContext#getContext()。查看详细路径信息:ohos.app.Context#getDatabaseDir()。 |
StoreConfig.Builder | public StoreConfig builder() | 对数据库进行配置,包括设置数据库名、存储模式、日志模式、同步模式,是否为只读,及数据库加密。 |
RdbOpenCallback | public abstract void onCreate(RdbStore store) | 数据库创建时被回调,开发者可以在该方法中初始化表结构,并添加一些应用使用到的初始化数据。 |
RdbOpenCallback | public abstract void onUpgrade(RdbStore store, int currentVersion, int targetVersion) | 数据库升级时被回调。 |
RdbOpenCallback | public void onDowngrade(RdbStore store, int currentVersion, int targetVersion) | 数据库降级时被回调。 |
DatabaseHelper | public RdbStore getRdbStore(StoreConfig config, int version, RdbOpenCallback openCallback, ResultSetHook resultSetHook) | 根据配置创建或打开数据库。 |
DatabaseHelper | public boolean deleteRdbStore(String name) | 删除指定的数据库。 |
数据库的增删改查
关系型数据库提供对本地数据增删改查操作的能力,相关API如下所示。
新增数据
关系型数据库提供了插入数据的接口,通过ValuesBucket输入要存储的数据,通过返回值判断是否插入成功,插入成功时返回最新插入数据所在的行号,失败时则返回-1。
类名 | 接口名 | 描述 |
---|---|---|
RdbStore | long insert(String table, ValuesBucket initialValues) | 向数据库插入数据。table:待添加数据的表名。initialValues:以ValuesBucket存储的待插入的数据。它提供一系列put方法,如putString(String columnName, String values),putDouble(String columnName, double value),用于向ValuesBucket中添加数据。 |
更新数据
调用更新接口,传入要更新的数据,并通过AbsRdbPredicates指定更新条件。该接口的返回值表示更新操作影响的行数。如果更新失败,则返回0。
类名 | 接口名 | 描述 |
---|---|---|
RdbStore | int update(ValuesBucket values, AbsRdbPredicates predicates) | 更新数据库表中符合谓词指定条件的数据。values:以ValuesBucket存储的要更新的数据。predicates:指定了更新操作的表名和条件。AbsRdbPredicates的实现类有两个:RdbPredicates和RawRdbPredicates。RdbPredicates:支持调用谓词提供的equalTo等接口,设置更新条件。RawRdbPredicates:仅支持设置表名、where条件子句、whereArgs三个参数,不支持equalTo等接口调用。 |
删除
调用删除接口,通过AbsRdbPredicates指定删除条件。该接口的返回值表示删除的数据行数,可根据此值判断是否删除成功。如果删除失败,则返回0。
类名 | 接口名 | 描述 |
---|---|---|
RdbStore | int delete(AbsRdbPredicates predicates) | 删除数据。predicates:Rdb谓词,指定了删除操作的表名和条件。AbsRdbPredicates的实现类有两个:RdbPredicates和RawRdbPredicates。RdbPredicates:支持调用谓词提供的equalTo等接口,设置更新条件。RawRdbPredicates:仅支持设置表名、where条件子句、whereArgs三个参数,不支持equalTo等接口调用。 |
查询
关系型数据库提供了两种查询数据的方式:
- 直接调用查询接口。使用该接口,会将包含查询条件的谓词自动拼接成完整的SQL语句进行查询操作,无需用户传入原生的SQL语句。
- 执行原生的SQL语句进行查询操作。
类名 | 接口名 | 描述 |
---|---|---|
RdbStore | ResultSet query(AbsRdbPredicates predicates, String[] columns) | 查询数据。predicates:谓词,可以设置查询条件。AbsRdbPredicates的实现类有两个:RdbPredicates和RawRdbPredicates。RdbPredicates:支持调用谓词提供的equalTo等接口,设置查询条件。RawRdbPredicates:仅支持设置表名、where条件子句、whereArgs三个参数,不支持equalTo等接口调用。columns:规定查询返回的列。 |
RdbStore | ResultSet querySql(String sql, String[] sqlArgs) | 执行原生的用于查询操作的SQL语句。sql:原生用于查询的sql语句。sqlArgs:sql语句中占位符参数的值,若select语句中没有使用占位符,该参数可以设置为null。 |
数据库谓词的使用
关系型数据库提供了用于设置数据库操作条件的谓词AbsRdbPredicates,其中包括两个实现子类RdbPredicates和RawRdbPredicates:
- RdbPredicates:开发者无需编写复杂的SQL语句,仅通过调用该类中条件相关的方法,如equalTo、notEqualTo、groupBy、orderByAsc、beginsWith等,就可自动完成SQL语句拼接,方便用户聚焦业务操作。
- RawRdbPredicates:可满足复杂SQL语句的场景,支持开发者自己设置where条件子句和whereArgs参数。不支持equalTo等条件接口的使用。
数据库谓词
类名 | 接口名 | 描述 |
---|---|---|
RdbPredicates | RdbPredicates equalTo(String field, String value) | 设置谓词条件,满足field字段与value值相等。 |
RdbPredicates | RdbPredicates notEqualTo(String field, String value) | 设置谓词条件,满足field字段与value值不相等。 |
RdbPredicates | RdbPredicates beginsWith(String field, String value) | 设置谓词条件,满足field字段以value值开头。 |
RdbPredicates | RdbPredicates between(String field, int low, int high) | 设置谓词条件,满足field字段在最小值low和最大值high之间。 |
RdbPredicates | RdbPredicates orderByAsc(String field) | 设置谓词条件,根据field字段升序排列。 |
RawRdbPredicates | void setWhereClause(String whereClause) | 设置where条件子句。 |
RawRdbPredicates | void setWhereArgs(List whereArgs) | 设置whereArgs参数,该值表示where子句中占位符的值。 |
查询结果集的使用
关系型数据库提供了查询返回的结果集ResultSet,其指向查询结果中的一行数据,供用户对查询结果进行遍历和访问。ResultSet对外API如下所示。
类名 | 接口名 | 描述 |
---|---|---|
ResultSet | boolean goTo(int offset) | 从结果集当前位置移动指定偏移量。 |
ResultSet | boolean goToRow(int position) | 将结果集移动到指定位置。 |
ResultSet | boolean goToNextRow() | 将结果集向后移动一行。 |
ResultSet | boolean goToPreviousRow() | 将结果集向前移动一行。 |
ResultSet | boolean isStarted() | 判断结果集是否被移动过。 |
ResultSet | boolean isEnded() | 判断结果集当前位置是否在最后一行之后。 |
ResultSet | boolean isAtFirstRow() | 判断结果集当前位置是否在第一行。 |
ResultSet | boolean isAtLastRow() | 判断结果集当前位置是否在最后一行。 |
ResultSet | int getRowCount() | 获取当前结果集中的记录条数。 |
ResultSet | int getColumnCount() | 获取结果集中的列数。 |
ResultSet | String getString(int columnIndex) | 获取当前行指定列的值,以String类型返回。 |
ResultSet | byte[] getBlob(int columnIndex) | 获取当前行指定列的值,以字节数组形式返回。 |
ResultSet | double getDouble(int columnIndex) | 获取当前行指定列的值,以double型返回。 |
数据库事务
关系型数据库提供事务机制,来保证用户操作的原子性。对单条数据进行数据库操作时,无需开启事务;插入大量数据时,开启事务可以保证数据的准确性。如果中途操作出现失败,会自动执行回滚操作。
类名 | 接口名 | 描述 |
---|---|---|
RdbStore | void beginTransaction() | 开启事务。 |
RdbStore | void markAsCommit() | 设置事务的标记为成功。 |
RdbStore | void endTransaction() | 结束事务。当调用此方法前若执行markAsCommit方法,事务会提交,否则事务会自动回滚。 |
数据库备份和恢复
用户可以将当前数据库的数据进行保存备份,还可以在需要的时候进行数据恢复。
类名 | 接口名 | 描述 |
---|---|---|
RdbStore | boolean restore(String srcName) | 数据库恢复接口,从指定的非加密数据库文件中恢复数据。 |
RdbStore | boolean restore(String srcName, byte[] srcEncryptKey, byte[] destEncryptKey) | 数据库恢复接口,从指定的数据库文件(加密和非加密均可)中恢复数据。 |
RdbStore | boolean backup(String destName) | 数据库备份接口,备份出的数据库文件是非加密的。 |
RdbStore | boolean backup(String destName, byte[] destEncryptKey) | 数据库备份接口,此方法经常用在备份出加密数据库场景。 |
5.3、关系型数据库开发
1、编写ability_main.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<Button
ohos:id="$+id:insertButton"
ohos:height="match_content"
ohos:width="match_parent"
ohos:top_margin="20vp"
ohos:background_element="gray"
ohos:text="插入数据"
ohos:text_size="20fp"
/>
<Button
ohos:id="$+id:updateButton"
ohos:height="match_content"
ohos:width="match_parent"
ohos:top_margin="20vp"
ohos:background_element="gray"
ohos:text="修改数据"
ohos:text_size="20fp"
/>
<Button
ohos:id="$+id:deleteButton"
ohos:height="match_content"
ohos:width="match_parent"
ohos:top_margin="20vp"
ohos:background_element="gray"
ohos:text="删除数据"
ohos:text_size="20fp"
/>
<Button
ohos:id="$+id:searchButton"
ohos:height="match_content"
ohos:width="match_parent"
ohos:top_margin="20vp"
ohos:background_element="gray"
ohos:text="查询数据"
ohos:text_size="20fp"
/>
<Button
ohos:id="$+id:searcMorehButton"
ohos:height="match_content"
ohos:width="match_parent"
ohos:top_margin="20vp"
ohos:background_element="gray"
ohos:text="查询多条数据"
ohos:text_size="20fp"
/>
</DirectionalLayout>
2、创建一个Student类
package com.sudojava.mydatabases.entity;
public class Student {
private Integer id;
private String name;
private Integer age;
private Double salary;
public Student(){
}
public Student(Integer id, String name, Integer age, Double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
public Student(String name, Integer age, Double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
3、创建数据库访问类
package com.sudojava.mydatabases.dbutils;
import com.sudojava.mydatabases.entity.Student;
import ohos.app.Context;
import ohos.data.DatabaseHelper;
import ohos.data.rdb.*;
import ohos.data.resultset.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class DBUtils {
private RdbStore rdbStore;
private DatabaseHelper helper;
public DBUtils(Context context){
helper = new DatabaseHelper(context);
initDB();
}
public void initDB(){
StoreConfig config = StoreConfig.newDefaultConfig("student.db");
RdbOpenCallback callback = new RdbOpenCallback() {
@Override
public void onCreate(RdbStore rdbStore) {
rdbStore.executeSql("create table if not exists student (id integer primary key autoincrement, name text not null, age integer, salary real)");
}
@Override
public void onUpgrade(RdbStore rdbStore, int i, int i1) {
}
};
rdbStore = helper.getRdbStore(config,1,callback);
}
/**
*
* @param student
* @return
*/
public boolean insertStudent(Student student){
ValuesBucket bucket = new ValuesBucket();
bucket.putInteger("age",student.getAge());
bucket.putString("name",student.getName());
bucket.putDouble("salary",student.getSalary());
long id = rdbStore.insert("student",bucket);
return id>0?true:false;
}
/**
*
* @param student
* @return
*/
public boolean updateStudent(Student student){
RdbPredicates predicates = new RdbPredicates("student");
predicates.equalTo("id",student.getId());
ValuesBucket bucket = new ValuesBucket();
bucket.putInteger("age",student.getAge());
bucket.putString("name",student.getName());
bucket.putDouble("salary",student.getSalary());
int id = rdbStore.update(bucket,predicates);
return id>0?true:false;
}
/**
*
* @param id
* @return
*/
public boolean deleteStudentByID(Integer id){
RdbPredicates predicates = new RdbPredicates("student");
predicates.equalTo("id",id);
int rowid = rdbStore.delete(predicates);
return rowid>0?true:false;
}
/**
*
* @param id
* @return
*/
public Student queryOneStudent(Integer id){
String[] queryName = new String[]{"id","age","name","salary"};
RdbPredicates predicates = new RdbPredicates("student");
predicates.equalTo("id",id);
ResultSet resultSet = rdbStore.query(predicates,queryName);
Student student = new Student();
if (resultSet.goToNextRow()){
student.setId(resultSet.getInt(resultSet.getColumnIndexForName("id")));
student.setAge(resultSet.getInt(resultSet.getColumnIndexForName("age")));
student.setName(resultSet.getString(resultSet.getColumnIndexForName("name")));
student.setSalary(resultSet.getDouble(resultSet.getColumnIndexForName("salary")));
}
return student;
}
/**
* 查询多条记录
* @param name
* @return
*/
public List<Student> queryMoreStudent(String name){
List<Student> list = new ArrayList<>();
String[] queryName = new String[]{"id","age","name","salary"};
RdbPredicates predicates = new RdbPredicates("student");
predicates.like("name","jack");
ResultSet resultSet = rdbStore.query(predicates,queryName);
while (resultSet.goToNextRow()){
Student student = new Student();
student.setId(resultSet.getInt(resultSet.getColumnIndexForName("id")));
student.setAge(resultSet.getInt(resultSet.getColumnIndexForName("age")));
student.setName(resultSet.getString(resultSet.getColumnIndexForName("name")));
student.setSalary(resultSet.getDouble(resultSet.getColumnIndexForName("salary")));
list.add(student);
}
return list;
}
}
4、编写MainAbilitySlice类
package com.sudojava.mydatabases.slice;
import com.sudojava.mydatabases.MainAbility;
import com.sudojava.mydatabases.ResourceTable;
import com.sudojava.mydatabases.dbutils.DBUtils;
import com.sudojava.mydatabases.entity.Student;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.window.dialog.ToastDialog;
import ohos.data.DatabaseHelper;
import ohos.data.rdb.RdbOpenCallback;
import ohos.data.rdb.RdbStore;
import ohos.data.rdb.StoreConfig;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainAbilitySlice extends AbilitySlice {
private final static HiLogLabel HI_LOG_LABEL =
new HiLogLabel(HiLog.LOG_APP,0x00201,"MainAbilitySlice");
private Button insert;
private Button update;
private Button delete;
private Button search;
private Button searchMore;
private DBUtils dbUtils;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
dbUtils = new DBUtils(MainAbilitySlice.this);
insert = (Button)findComponentById(ResourceTable.Id_insertButton);
insert.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
getUITaskDispatcher().asyncDispatch(new Runnable() {
@Override
public void run() {
Student student = new Student("jack",24,3400.0);
boolean flag = dbUtils.insertStudent(student);
if (flag){
HiLog.info(HI_LOG_LABEL,"--->>"+flag);
}else{
HiLog.info(HI_LOG_LABEL,"--插入失败->>");
}
}
});
}
});
update = (Button)findComponentById(ResourceTable.Id_updateButton);
update.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Student student = new Student(1,"rose",34,3521.0);
boolean flag = dbUtils.updateStudent(student);
if (flag){
HiLog.info(HI_LOG_LABEL,"--->>"+flag);
}else{
HiLog.info(HI_LOG_LABEL,"--修改失败->>");
}
}
});
delete = (Button)findComponentById(ResourceTable.Id_deleteButton);
delete.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
boolean flag = dbUtils.deleteStudentByID(1);
if (flag){
HiLog.info(HI_LOG_LABEL,"--->>"+flag);
}else{
HiLog.info(HI_LOG_LABEL,"--删除失败->>");
}
}
});
search = (Button)findComponentById(ResourceTable.Id_searchButton);
search.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
getUITaskDispatcher().asyncDispatch(new Runnable() {
@Override
public void run() {
Student student = dbUtils.queryOneStudent(1);
HiLog.info(HI_LOG_LABEL,"--查询数据->>"+student);
}
});
}
});
searchMore = (Button)findComponentById(ResourceTable.Id_searcMorehButton);
searchMore.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
List<Student> list = dbUtils.queryMoreStudent("jack");
HiLog.info(HI_LOG_LABEL,"--查询数据-->>"+list);
}
});
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
5.4、对象型数据库开发
对象关系映射数据库适用于开发者使用的数据可以分解为一个或多个对象,且需要对数据进行增删改查等操作,但是不希望编写过于复杂的SQL语句的场景。
ORM对象关系映射
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射 的元数据,将程序中的对象自动持久化到关系数据库中。
几乎所有的程序里面,都存在对象和关系数据 库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。
当你开发一个应用程序的时候(不使用O/R Mapping),你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息,等等。
数据库和表的创建
-
创建数据库。开发者需要定义一个表示数据库的类,继承OrmDatabase,再通过@Database注解内的entities属性指定哪些数据模型类属于这个数据库。
属性:
- version:数据库版本号。
- entities:数据库内包含的表。
-
创建数据表。开发者可通过创建一个继承了OrmObject并用@Entity注解的类,获取数据库实体对象,也就是表的对象。
属性:
- tableName:表名。
- primaryKeys:主键名,一个表里只能有一个主键,一个主键可以由多个字段组成。
- foreignKeys:外键列表。
- indices:索引列表。
注解对应的表
接口名称 | 描述 |
---|---|
@Database | 被@Database注解且继承了OrmDatabase的类对应数据库类。 |
@Entity | 被@Entity注解且继承了OrmObject的类对应数据表类。 |
@Column | 被@Column注解的变量对应数据表的字段。 |
@PrimaryKey | 被@PrimaryKey注解的变量对应数据表的主键。 |
@ForeignKey | 被@ForeignKey注解的变量对应数据表的外键。 |
@Index | 被@Index注解的内容对应数据表索引的属性。 |
打开数据库和数据库加密
- 打开数据库。开发者通过getOrmContext打开数据库。
打开数据库的API
类名 | 接口名 | 描述 |
---|---|---|
DatabaseHelper | DatabaseHelper(Context context) | DatabaseHelper是数据库操作的辅助类,当数据库创建成功后,数据库文件将存储在由上下文指定的目录里。获取上下文参考方法:context入参类型为ohos.app.Context,注意不要使用slice.getContext()来获取context,请直接传入slice,否则会出现找不到类的报错。查看详细路径信息:ohos.app.Context#getDatabaseDir()。 |
DatabaseHelper | public OrmContext getOrmContext(String alias, String name, Class ormDatabase, OrmMigration… migrations) | 打开数据库,alias数据库别名,name数据库名称,ormDatabase数据库对应类,migrations数据库升级类。 |
DatabaseHelper | public OrmContext getOrmContext(OrmConfig ormConfig, Class ormDatabase, OrmMigration… migrations) | 打开数据库,ormConfig数据库配置,ormDatabase数据库对应类,migrations数据库升级类。 |
DatabaseHelper | public OrmContext getOrmContext(String alias) | 根据别名打开数据库。 |
- 数据库加密。对象关系映射数据库提供数据库加密的能力,创建加密数据库时传入指定密钥,后续打开加密数据库时,需要传入正确密钥。
数据库传入密钥API
类名 | 接口名 | 描述 |
---|---|---|
OrmConfig.Builder | public OrmConfig.Builder setEncryptKey(byte[] encryptKey) | 为数据库配置类设置数据库加密密钥,创建或打开数据库时传入包含数据库加密密钥的配置类,即可创建或打开加密数据库。 |
对象数据的增删改查
通过对象数据操作接口,开发者可以对对象数据进行增删改查操作。
对象数据操作API
类名 | 接口名 | 描述 |
---|---|---|
OrmContext | boolean insert(T object) | 添加方法。 |
OrmContext | boolean update(T object) | 更新方法。 |
OrmContext | List query(OrmPredicates predicates) | 查询方法。 |
OrmContext | boolean delete(T object) | 删除方法。 |
OrmContext | OrmPredicates where(Class clz) | 设置谓词方法。 |
实战案例:对用户表的CRUD操作
第一步:编写一个ability_main.xml
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">
<Button
ohos:id="$+id:insertButton"
ohos:height="match_content"
ohos:width="match_parent"
ohos:top_margin="20vp"
ohos:background_element="gray"
ohos:text="插入数据"
ohos:text_size="20fp"
/>
<Button
ohos:id="$+id:updateButton"
ohos:height="match_content"
ohos:width="match_parent"
ohos:top_margin="20vp"
ohos:background_element="gray"
ohos:text="修改数据"
ohos:text_size="20fp"
/>
<Button
ohos:id="$+id:deleteButton"
ohos:height="match_content"
ohos:width="match_parent"
ohos:top_margin="20vp"
ohos:background_element="gray"
ohos:text="删除数据"
ohos:text_size="20fp"
/>
<Button
ohos:id="$+id:searcMorehButton"
ohos:height="match_content"
ohos:width="match_parent"
ohos:top_margin="20vp"
ohos:background_element="gray"
ohos:text="查询多条数据"
ohos:text_size="20fp"
/>
</DirectionalLayout>
第二步:配置工程的build.gradle文件
使用注解处理器的模块为“com.huawei.ohos.hap”模块,则需要在模块的“build.gradle”文件的ohos节点中添加以下配置:
compileOptions{
annotationEnabled true
}
使用注解处理器的模块为“com.huawei.ohos.library”模块,则需要在模块的“build.gradle”文件的“dependencies”节点中配置注解处理器。
查看“orm_annotations_java.jar”、“orm_annotations_processor_java.jar” 、“javapoet_java.jar”这3个jar包在HUAWEI SDK中的Sdk/java/x.x.x.xx/build-tools/lib/目录,并将目录的这三个jar包导进来。
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
testImplementation 'junit:junit:4.13'
compile files(
"./libs/javapoet_java.jar",
"./libs/orm_annotations_java.jar",
"./libs/orm_annotations_processor_java.jar")
annotationProcessor files(
"./libs/javapoet_java.jar",
"./libs/orm_annotations_java.jar",
"./libs/orm_annotations_processor_java.jar")
ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.200'
}
直接将以上三个包拷贝到工程的libs目录下,然后倒入进来。
第三步:创建一个User类
package com.sudojava.databaseorm.bean;
import ohos.data.orm.OrmObject;
import ohos.data.orm.annotation.Column;
import ohos.data.orm.annotation.Entity;
import ohos.data.orm.annotation.Index;
import ohos.data.orm.annotation.PrimaryKey;
//对应的数据库的表名
@Entity(tableName = "user")
public class User extends OrmObject {
@PrimaryKey(autoGenerate = true)
private Integer userId;
private String firstName;
private String lastName;
private int age;
private double balance;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", age=" + age +
", balance=" + balance +
'}';
}
}
第四步:创建一个UserStore类
package com.sudojava.databaseorm.db;
import com.sudojava.databaseorm.bean.User;
import ohos.data.orm.OrmDatabase;
import ohos.data.orm.annotation.Database;
@Database(entities = {User.class},version = 1)
public abstract class UserStore extends OrmDatabase {
}
第五步:编写数据库封装类
package com.sudojava.databaseorm.db;
import ohos.app.Context;
import ohos.data.DatabaseHelper;
import ohos.data.orm.OrmContext;
import ohos.data.orm.OrmMigration;
import ohos.data.rdb.RdbOpenCallback;
import ohos.data.rdb.RdbStore;
import ohos.data.rdb.StoreConfig;
import ohos.data.resultset.ResultSetHook;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
public class DBConfig {
private final static HiLogLabel HI_LOG_LABEL =
new HiLogLabel(HiLog.LOG_APP,0x00201,"MainAbilitySlice");
private String database_name="mydb.db";
private String database_name_alias="mydb";
private DatabaseHelper helper;
private OrmContext ormContext;
private StoreConfig config;
private RdbStore store;
public DBConfig(Context context) {
helper = new DatabaseHelper(context);
}
/**
*
* @param ObjectStore
* @return
*/
public OrmContext getConnectionContext(Class ObjectStore){
this.ormContext = helper.getOrmContext(database_name_alias,database_name,ObjectStore,new OrmMigration[0]);
return this.ormContext;
}
/**
* 创建建表SQL语句
* @param sql
* @return
*/
public RdbStore getConnectionStoreWithSql(final String sql){
this.config = StoreConfig.newDefaultConfig(this.database_name);
RdbOpenCallback callback = new RdbOpenCallback() {
@Override
public void onCreate(RdbStore rdbStore) {
rdbStore.executeSql(sql);
}
@Override
public void onUpgrade(RdbStore rdbStore, int i, int i1) {
}
};
this.store = helper.getRdbStore(this.config,1,callback,(ResultSetHook)null);
return this.store;
}
}
第六步:创建一个User对应的数据库管理类
package com.sudojava.databaseorm.utils;
import com.sudojava.databaseorm.bean.User;
import com.sudojava.databaseorm.db.DBConfig;
import com.sudojava.databaseorm.db.UserStore;
import ohos.app.Context;
import ohos.data.orm.OrmContext;
import ohos.data.orm.OrmPredicates;
import ohos.data.rdb.RdbPredicates;
import ohos.data.rdb.ValuesBucket;
import ohos.data.resultset.ResultSet;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.util.ArrayList;
import java.util.List;
public class UserManager {
private final static HiLogLabel HI_LOG_LABEL =
new HiLogLabel(HiLog.LOG_APP,0x00201,"UserManager");
private Context context;
private DBConfig config;
private OrmContext ormContext;
public UserManager(Context context){
config = new DBConfig(context);
ormContext = config.getConnectionContext(UserStore.class);
}
/**
*
* @param lastName
* @return
*/
public List<User> queryUser(String lastName){
List<User> list = new ArrayList<>();
OrmPredicates predicates = ormContext.where(User.class).equalTo("lastName",lastName);
list = ormContext.query(predicates);
HiLog.info(HI_LOG_LABEL,"UserManager:"+list);
return list;
//dbOrmContext.flush(context);
}
public User queryOne(Integer userId){
OrmPredicates predicates = ormContext.where(User.class).equalTo("userId",userId);
User user = new User();
ResultSet resultSet = ormContext.query(predicates,new String[]{"userId","firstName","lastName","age","balance"});
if (resultSet.goToNextRow()){
user.setUserId(resultSet.getInt(resultSet.getColumnIndexForName("userId")));
user.setAge(resultSet.getInt(resultSet.getColumnIndexForName("age")));
user.setLastName(resultSet.getString(resultSet.getColumnIndexForName("lastName")));
user.setFirstName(resultSet.getString(resultSet.getColumnIndexForName("firstName")));
user.setBalance(resultSet.getDouble(resultSet.getColumnIndexForName("balance")));
}
return user;
}
/**
*
* @param user
* @return
*/
public boolean deleteUser(Integer userId){
OrmPredicates predicates = ormContext.where(User.class).equalTo("userId",userId);
int flag = ormContext.delete(predicates);
HiLog.info(HI_LOG_LABEL,"UserManager:"+flag);
return flag>0?true:false;
}
/**
*
* @param user
* @return
*/
public boolean insertUser(User user){
boolean flag = ormContext.insert(user);
HiLog.info(HI_LOG_LABEL,"UserManager:"+flag);
return ormContext.flush();
}
/**
*
* @param user
* @return
*/
public boolean updateUser(Integer userId){
ValuesBucket bucket = new ValuesBucket();
bucket.putInteger("age", 11);
bucket.putString("firstName", "jack");
bucket.putString("lastName", "San");
bucket.putDouble("balance", 890.51);
OrmPredicates predicates = ormContext.where(User.class).equalTo("userId",userId);
int row_id = ormContext.update(predicates,bucket);
HiLog.info(HI_LOG_LABEL,"UserManager:"+row_id);
return row_id>0?true:false;
}
}
第七步:编写MainAbilitySlice类
package com.sudojava.databaseorm.slice;
import com.sudojava.databaseorm.ResourceTable;
import com.sudojava.databaseorm.bean.User;
import com.sudojava.databaseorm.db.UserStore;
import com.sudojava.databaseorm.utils.UserManager;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.data.DatabaseHelper;
import ohos.data.orm.OrmContext;
import ohos.data.orm.OrmPredicates;
import ohos.data.rdb.RdbStore;
import ohos.data.resultset.ResultSetHook;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.util.ArrayList;
import java.util.List;
public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener {
private final static HiLogLabel HI_LOG_LABEL =
new HiLogLabel(HiLog.LOG_APP,0x00201,"MainAbilitySlice");
private UserManager manager;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
manager = new UserManager(MainAbilitySlice.this);
Button insertButton = (Button)findComponentById(ResourceTable.Id_insertButton);
Button deleteButton = (Button)findComponentById(ResourceTable.Id_deleteButton);
Button searchButton = (Button)findComponentById(ResourceTable.Id_searcMorehButton);
Button updateButton = (Button)findComponentById(ResourceTable.Id_updateButton);
insertButton.setClickedListener(this::onClick);
searchButton.setClickedListener(this::onClick);
deleteButton.setClickedListener(this::onClick);
updateButton.setClickedListener(this::onClick);
}
@Override
public void onClick(Component component) {
switch (component.getId()){
case ResourceTable.Id_insertButton:
User user = new User();
user.setFirstName("Zhang");
user.setLastName("San");
user.setAge(29);
user.setBalance(100.51);
boolean flag = manager.insertUser(user);
HiLog.info(HI_LOG_LABEL,"---->>"+flag);
break;
case ResourceTable.Id_searcMorehButton:
List<User> list = manager.queryUser("San");
HiLog.info(HI_LOG_LABEL,"---->>"+list);
break;
case ResourceTable.Id_deleteButton:
boolean flag1 = manager.deleteUser(1);
HiLog.info(HI_LOG_LABEL,"---->>"+flag1);
break;
case ResourceTable.Id_updateButton:
boolean flag2 = manager.updateUser(1);
HiLog.info(HI_LOG_LABEL,"---->>"+flag2);
break;
}
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
5.5、Data Ability的使用
使用Data模板的Ability(以下简称“Data”)有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。Data既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。
数据的存放形式多样,可以是数据库,也可以是磁盘上的文件。Data对外提供对数据的增、删、改、查,以及打开文件等接口,这些接口的具体实现由开发者提供。
URI介绍
Data的提供方和使用方都通过URI(Uniform Resource Identifier)来标识一个具体的数据,例如数据库中的某个表或磁盘上的某个文件。HarmonyOS的URI仍基于URI通用标准,格式如下:
- scheme:协议方案名,固定为“dataability”,代表Data Ability所使用的协议类型。
- authority:设备ID。如果为跨设备场景,则为目标设备的ID;如果为本地设备场景,则不需要填写。
- path:资源的路径信息,代表特定资源的位置信息。
- query:查询参数。
- fragment:可以用于指示要访问的子资源。
URI示例:
-
跨设备场景:dataability://device_id/com.domainname.dataability.persondata/person/10
-
本地设备:dataability:///com.domainname.dataability.persondata/person/10
说明
本地设备的“device_id”字段为空,因此在“dataability:”后面有三个“/”。
使用Data模板的Ability形式仍然是Ability,因此,开发者需要为应用添加一个或多个Ability的子类,来提供程序与其他应用之间的接口。Data为结构化数据和文件提供了不同API接口供用户使用,因此,开发者需要首先确定好使用何种类型的数据。本章节主要讲述了创建Data的基本步骤和需要使用的接口。
Data提供方可以自定义数据的增、删、改、查,以及文件打开等功能,并对外提供这些接口。
确定数据存储方式
确定数据的存储方式,Data支持以下两种数据形式:
- 文件数据:如文本、图片、音乐等。
- 结构化数据:如数据库等。
实战案例:实现对Person表的CRUD操作
第一步:编写ability_main.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">
<Button
ohos:id="$+id:button"
ohos:text_size="25vp"
ohos:text="插入数据"
ohos:background_element="gray"
ohos:height="match_content"
ohos:width="match_parent"/>
<Component
ohos:height="15vp"
ohos:width="match_parent"/>
<Button
ohos:id="$+id:search"
ohos:text_size="25vp"
ohos:text="查询单条数据"
ohos:background_element="gray"
ohos:height="match_content"
ohos:width="match_parent"/>
<Component
ohos:height="15vp"
ohos:width="match_parent"/>
<Button
ohos:id="$+id:searchmore"
ohos:text_size="25vp"
ohos:text="查询多条数据"
ohos:background_element="gray"
ohos:height="match_content"
ohos:width="match_parent"/>
<Component
ohos:height="15vp"
ohos:width="match_parent"/>
<Button
ohos:id="$+id:delete"
ohos:text_size="25vp"
ohos:text="删除单条数据"
ohos:background_element="gray"
ohos:height="match_content"
ohos:width="match_parent"/>
<Component
ohos:height="15vp"
ohos:width="match_parent"/>
<Button
ohos:id="$+id:update"
ohos:text_size="25vp"
ohos:text="修改数据"
ohos:background_element="gray"
ohos:height="match_content"
ohos:width="match_parent"/>
</DirectionalLayout>
第二步:创建Person对应的实体类
package com.persondata.entity;
public class Person {
private Integer id;
private String name;
private String gender;
private Integer age;
public Person(){
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
}
第三步:定一个常量Const类
package com.persondata.utils;
public class Const {
public static final String DB_NAME = "persondataability.db";
public static final String DB_TAB_NAME = "person";
public static final String BASE_URI = "dataability:///com.persondata.PersonDataAbility";
public static final String DATA_PATH = "/person";
public static final String DB_COLUMN_PERSON_ID = "id";
public static final String DB_COLUMN_NAME = "name";
public static final String DB_COLUMN_GENDER = "gender";
public static final String DB_COLUMN_AGE = "age";
public static final int DB_VERSION = 1;
}
第四步:创建 PersonDataAbility类
package com.persondata;
import com.persondata.utils.Const;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.content.Intent;
import ohos.data.DatabaseHelper;
import ohos.data.dataability.DataAbilityPredicates;
import ohos.data.dataability.DataAbilityUtils;
import ohos.data.rdb.*;
import ohos.data.resultset.ResultSet;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.net.Uri;
public class PersonDataAbility extends Ability {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
private StoreConfig config = StoreConfig.newDefaultConfig(Const.DB_NAME);
private RdbStore rdbStore;
private RdbOpenCallback rdbOpenCallback = new RdbOpenCallback() {
@Override
public void onCreate(RdbStore store) {
store.executeSql("create table if not exists "
+ Const.DB_TAB_NAME + " ("
+ Const.DB_COLUMN_PERSON_ID + " integer primary key, "
+ Const.DB_COLUMN_NAME + " text not null, "
+ Const.DB_COLUMN_GENDER + " text not null, "
+ Const.DB_COLUMN_AGE + " integer)");
}
@Override
public void onUpgrade(RdbStore store, int oldVersion, int newVersion) {
}
};
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
HiLog.info(LABEL_LOG, "PersonDataAbility onStart");
DatabaseHelper databaseHelper = new DatabaseHelper(this);
rdbStore = databaseHelper.getRdbStore(config, Const.DB_VERSION, rdbOpenCallback, null);
}
@Override
public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, Const.DB_TAB_NAME);
ResultSet resultSet = rdbStore.query(rdbPredicates, columns);
if (resultSet == null) {
HiLog.info(LABEL_LOG, "resultSet is null");
}
return resultSet;
}
@Override
public int insert(Uri uri, ValuesBucket value) {
HiLog.info(LABEL_LOG, "PersonDataAbility insert");
String path = uri.getLastPath();
if (!"person".equals(path)) {
HiLog.info(LABEL_LOG, "DataAbility insert path is not matched");
return -1;
}
int index = (int) rdbStore.insert(Const.DB_TAB_NAME, value);
DataAbilityHelper.creator(this, uri).notifyChange(uri);
return index;
}
@Override
public int delete(Uri uri, DataAbilityPredicates predicates) {
RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, Const.DB_TAB_NAME);
int index = rdbStore.delete(rdbPredicates);
HiLog.info(LABEL_LOG, "delete: " + index);
DataAbilityHelper.creator(this,uri).notifyChange(uri);
return index;
}
@Override
public int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates) {
RdbPredicates rdbPredicates = DataAbilityUtils.createRdbPredicates(predicates, Const.DB_TAB_NAME);
int index = rdbStore.update(value,rdbPredicates);
HiLog.info(LABEL_LOG, "update: " + index);
DataAbilityHelper.creator(this,uri).notifyChange(uri);
return index;
}
}
第五步:配置config.json
{
"app": {
"bundleName": "com.persondata",
"vendor": "persondata",
"version": {
"code": 1000000,
"name": "1.0.0"
}
},
"deviceConfig": {},
"module": {
"package": "com.persondata",
"name": ".MyApplication",
"mainAbility": "com.persondata.MainAbility",
"deviceType": [
"phone"
],
"reqPermissions": [
{
"name": "com.persondata.mypermission"
}
],
"distro": {
"deliveryWithInstall": true,
"moduleName": "entry",
"moduleType": "entry",
"installationFree": false
},
"abilities": [
{
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
],
"orientation": "unspecified",
"name": "com.persondata.MainAbility",
"icon": "$media:icon",
"description": "$string:mainability_description",
"label": "$string:entry_MainAbility",
"type": "page",
"launchType": "standard"
},
{
"name": ".PersonDataAbility",
"type": "data",
"visible": true,
"uri": "dataability://com.persondata.PersonDataAbility",
"permissions": [
"com.persondata.mypermission"
]
}
]
}
}
第六步:封装一个DBManager类
package com.persondata.db;
import com.persondata.entity.Person;
import com.persondata.utils.Const;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.ability.DataAbilityRemoteException;
import ohos.app.Context;
import ohos.data.dataability.DataAbilityPredicates;
import ohos.data.rdb.ValuesBucket;
import ohos.data.resultset.ResultSet;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.net.Uri;
import java.util.ArrayList;
import java.util.List;
public class DBManager {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0xD001100, "DBManager");
private DataAbilityHelper helper;
public DBManager(Context context){
helper = DataAbilityHelper.creator(context);
}
public Person queryPerson(){
Person person = new Person();
String[] columns = new String[] {Const.DB_COLUMN_PERSON_ID,
Const.DB_COLUMN_NAME, Const.DB_COLUMN_GENDER, Const.DB_COLUMN_AGE};
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between(Const.DB_COLUMN_AGE,15,40);
try{
ResultSet resultSet = helper.query(Uri.parse(Const.BASE_URI+Const.DATA_PATH),columns,predicates);
if (resultSet==null||resultSet.getRowCount()==0){
HiLog.info(LABEL_LOG,"query : resultset is null or no result found");
return null;
}
//将数据库的指针定位到第一条记录
resultSet.goToFirstRow();
do {
person.setId(resultSet.getInt(resultSet.getColumnIndexForName(Const.DB_COLUMN_PERSON_ID)));
person.setName(resultSet.getString(resultSet.getColumnIndexForName(Const.DB_COLUMN_NAME)));
person.setGender(resultSet.getString(resultSet.getColumnIndexForName(Const.DB_COLUMN_GENDER)));
person.setAge(resultSet.getInt(resultSet.getColumnIndexForName(Const.DB_COLUMN_AGE)));
}while (resultSet.goToNextRow());
}catch (Exception e){
HiLog.error(LABEL_LOG, "query: dataRemote exception | illegalStateException");
}
return person;
}
/**
*
* @return
*/
public List<Person> queryAllPerson(){
List<Person> list = new ArrayList<>();
String[] columns = new String[] {Const.DB_COLUMN_PERSON_ID,
Const.DB_COLUMN_NAME, Const.DB_COLUMN_GENDER, Const.DB_COLUMN_AGE};
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.greaterThan(Const.DB_COLUMN_PERSON_ID,0);
try{
ResultSet resultSet = helper.query(Uri.parse(Const.BASE_URI+Const.DATA_PATH),columns,predicates);
if (resultSet==null||resultSet.getRowCount()==0){
HiLog.info(LABEL_LOG,"query : resultset is null or no result found");
return null;
}
while (resultSet.goToNextRow()){
Person person = new Person();
person.setId(resultSet.getInt(resultSet.getColumnIndexForName(Const.DB_COLUMN_PERSON_ID)));
person.setName(resultSet.getString(resultSet.getColumnIndexForName(Const.DB_COLUMN_NAME)));
person.setGender(resultSet.getString(resultSet.getColumnIndexForName(Const.DB_COLUMN_GENDER)));
person.setAge(resultSet.getInt(resultSet.getColumnIndexForName(Const.DB_COLUMN_AGE)));
list.add(person);
}
}catch (Exception e){
HiLog.error(LABEL_LOG, "query: dataRemote exception | illegalStateException");
}
return list;
}
/**
* 执行删除操作
* @return
*/
public boolean delete(){
boolean flag = false;
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.equalTo(Const.DB_COLUMN_PERSON_ID,1001);
try{
flag = helper.delete(Uri.parse(Const.BASE_URI+Const.DATA_PATH),predicates)>0?true:false;
HiLog.info(LABEL_LOG, "delete successful");
}catch (DataAbilityRemoteException | IllegalStateException exception){
HiLog.error(LABEL_LOG, "delete: dataRemote exception|illegalStateException");
}
return flag;
}
/**
*
* @param person
* @return
*/
public boolean insertPerson(Person person){
boolean flag = false;
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putInteger(Const.DB_COLUMN_PERSON_ID, person.getId());
valuesBucket.putString(Const.DB_COLUMN_NAME, person.getName());
valuesBucket.putString(Const.DB_COLUMN_GENDER, person.getGender());
valuesBucket.putInteger(Const.DB_COLUMN_AGE, person.getAge());
try{
flag = helper.insert(Uri.parse(Const.BASE_URI+Const.DATA_PATH),valuesBucket)>0?true:false;
HiLog.info(LABEL_LOG, "insert successful");
}catch (DataAbilityRemoteException | IllegalStateException exception){
HiLog.error(LABEL_LOG, "insert: dataRemote exception|illegalStateException");
}
return flag;
}
/**
*
* @param person
* @return
*/
public boolean updatePerson(Person person){
boolean flag = false;
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putInteger(Const.DB_COLUMN_PERSON_ID, person.getId());
valuesBucket.putString(Const.DB_COLUMN_NAME, person.getName());
valuesBucket.putString(Const.DB_COLUMN_GENDER, person.getGender());
valuesBucket.putInteger(Const.DB_COLUMN_AGE, person.getAge());
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.equalTo(Const.DB_COLUMN_PERSON_ID,person.getId());
try{
flag = helper.update(Uri.parse(Const.BASE_URI+Const.DATA_PATH),valuesBucket,predicates)>0?true:false;
HiLog.info(LABEL_LOG, "insert successful");
}catch (DataAbilityRemoteException | IllegalStateException exception){
HiLog.error(LABEL_LOG, "insert: dataRemote exception|illegalStateException");
}
return flag;
}
}
第七步:创建MainAbilitySlice类
package com.qf.personability.slice;
import com.qf.personability.ResourceTable;
import com.qf.personability.db.DBManager;
import com.qf.personability.entity.Person;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.util.List;
public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener {
private static final HiLogLabel HI_LOG_LABEL = new HiLogLabel(HiLog.LOG_APP,0xD001100,"MainAbilitySlice");
private DBManager manager;
private Button insert;
private Button delete;
private Button update;
private Button search;
private Button searchMore;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
manager = new DBManager(MainAbilitySlice.this);
insert = (Button)findComponentById(ResourceTable.Id_insert);
delete = (Button)findComponentById(ResourceTable.Id_delete);
update = (Button)findComponentById(ResourceTable.Id_update);
search = (Button)findComponentById(ResourceTable.Id_search);
searchMore = (Button)findComponentById(ResourceTable.Id_searchmore);
insert.setClickedListener(this::onClick);
delete.setClickedListener(this::onClick);
update.setClickedListener(this::onClick);
search.setClickedListener(this::onClick);
searchMore.setClickedListener(this::onClick);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
@Override
public void onClick(Component component) {
boolean flag;
switch (component.getId()){
case ResourceTable.Id_insert:
Person person = new Person();
person.setId(1001);
person.setName("jack");
person.setGender("man");
person.setAge(23);
flag = manager.insertPerson(person);
HiLog.info(HI_LOG_LABEL,"---->>"+flag);
break;
case ResourceTable.Id_delete:
flag = manager.deletePerson(1001);
HiLog.info(HI_LOG_LABEL,"---->>"+flag);
break;
case ResourceTable.Id_update:
Person person1 = new Person();
person1.setId(1001);
person1.setName("rose");
person1.setGender("girl");
person1.setAge(24);
flag = manager.updatePerson(person1);
HiLog.info(HI_LOG_LABEL,"---->>"+flag);
break;
case ResourceTable.Id_search:
Person person2 = manager.querySinglePerson(1001);
HiLog.info(HI_LOG_LABEL,"---->>"+person2);
break;
case ResourceTable.Id_searchmore:
List<Person> list = manager.queryMorePerson(10,100);
HiLog.info(HI_LOG_LABEL,"---->>"+list);
break;
}
}
}
5.6、Preferences轻量数据库存储
轻量级数据存储适用于对Key-Value结构的数据进行存取和持久化操作。应用运行时全量数据将会被加载在内存中的,使得访问速度更快,存取效率更高。如果对数据持久化,数据最终会落盘到文本文件中,建议在开发过程中减少落盘频率,即减少对持久化文件的读写次数。
轻量级数据存储主要用于保存应用的一些常用配置,并不适合存储大量数据和频繁改变数据的场景。用户的数据保存在文件中,可以持久化的存储在设备上。需要注意的是用户访问的实例包含文件所有数据,并一直加载在设备的内存中,并通过轻量级数据存储的API完成数据操作。
基本概念
-
Key-Value数据结构
一种键值结构数据类型。Key是不重复的关键字,Value是数据值。
-
非关系型数据库
区别于关系数据库,不保证遵循ACID(Atomic、Consistency、Isolation及Durability)特性,不采用关系模型来组织数据,数据之间无关系,扩展性好。
轻量级数据存储向本地应用提供的API支持本地应用读写数据及观察数据变化。数据存储形式为键值对,键的类型为字符串型,值的存储数据类型包括整型、字符串型、布尔型、浮点型、长整型、字符串型Set集合。
运行机制
- 本模块提供轻量级数据存储的操作类,应用通过这些操作类完成数据库操作。
- 借助DatabaseHelper API,应用可以将指定文件的内容加载到Preferences实例,每个文件最多有一个Preferences实例,系统会通过静态容器将该实例存储在内存中,直到应用主动从内存中移除该实例或者删除该文件。
- 获取到文件对应的Preferences实例后,应用可以借助Preferences API,从Pref
约束和限制
- Key键为String类型,要求非空且长度不超过80个字符。
- Value值为String类型时,可以为空但是长度不超过8192个字符。
- Value值为字符串型Set集合类型时,要求集合元素非空且长度不超过8192个字符。
- 存储的数据量应该是轻量级的,建议存储的数据不超过一万条,否则会在内存方面产生较大的开销。
开发步骤文档说明:
- 创建数据库
类名 | 接口名 | 描述 |
---|---|---|
DatabaseHelper | DatabaseHelper(Context context) | DatabaseHelper是数据库操作的辅助类,当数据库创建成功后,数据库文件将存储在由上下文指定的目录里。数据库文件存储的路径会因指定不同的上下文存在差异。获取上下文参考方法:ohos.app.Context#getApplicationContext()、ohos.app.AbilityContext#getContext()。查看详细路径信息:ohos.app.Context#getDatabaseDir()。 |
DatabaseHelper | Preferences getPreferences(String name) | 获取文件对应的Preferences单实例,用于数据操作。 |
- 查询数据
通过调用get系列的方法,可以查询不同类型的数据。
类名 | 接口名 | 描述 |
---|---|---|
Preferences | int getInt(String key, int defValue) | 获取键对应的int类型的值。 |
Preferences | float getFloat(String key, float defValue) | 获取键对应的float类型的值。 |
- 插入数据
通过put系列的方法,可以修改Preferences实例中的数据,通过flush或者flushSync将Preferences实例持久化。
类名 | 接口名 | 描述 |
---|---|---|
Preferences | Preferences putInt(String key, int value) | 设置Preferences实例中键对应的int类型的值。 |
Preferences | Preferences putString(String key, String value) | 设置Preferences实例中键对应的String类型的值。 |
Preferences | void flush() | 将Preferences实例异步写入文件。 |
Preferences | boolean flushSync() | 将Preferences实例同步写入文件。 |
- 观察数据变化
轻量级数据存储还提供了一系列的接口变化回调,用于应用响应数据的变化。开发者可以通过重写onChange方法来定义观察者的行为。
类名 | 接口名 | 描述 |
---|---|---|
Preferences | void registerObserver(PreferencesObserver preferencesObserver) | 注册观察者,用于观察数据变化。 |
Preferences | void unRegisterObserver(PreferencesObserver preferencesObserver) | 注销观察者。 |
Preferences.PreferencesObserver | void onChange(Preferences preferences, String key) | 观察者的回调方法,任意数据变化都会回调该方法。 |
- 删除数据文件
通过调用以下两种接口,可以删除数据文件。
类名 | 接口名 | 描述 |
---|---|---|
DatabaseHelper | boolean deletePreferences(String name) | 删除文件和文件对应的Preferences单实例。 |
DatabaseHelper | void removePreferencesFromCache(String name) | 删除文件对应的Preferences单实例。 |
- 移除数据库文件
类名 | 接口名 | 描述 |
---|---|---|
DatabaseHelper | boolean movePreferences(Context sourceContext, String sourceName, String targetName) | 移动数据库文件。 |
综合案例:实现对轻量数据库的CRUD操作
第一步:编写ability_main.xml
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">
<Button
ohos:id="$+id:insert"
ohos:text="保存数据"
ohos:text_size="25fp"
ohos:background_element="gray"
ohos:height="match_content"
ohos:width="match_parent"/>
<Component
ohos:height="10vp"
ohos:width="match_parent"/>
<Button
ohos:id="$+id:viewpath"
ohos:text="查看数据库路径"
ohos:text_size="25fp"
ohos:background_element="gray"
ohos:height="match_content"
ohos:width="match_parent"/>
<Component
ohos:height="10vp"
ohos:width="match_parent"/>
<Component
ohos:height="10vp"
ohos:width="match_parent"/>
<Button
ohos:id="$+id:update"
ohos:text="修改数据"
ohos:text_size="25fp"
ohos:background_element="gray"
ohos:height="match_content"
ohos:width="match_parent"/>
<Component
ohos:height="10vp"
ohos:width="match_parent"/>
<Button
ohos:id="$+id:search"
ohos:text="查询数据"
ohos:text_size="25fp"
ohos:background_element="gray"
ohos:height="match_content"
ohos:width="match_parent"/>
<Component
ohos:height="10vp"
ohos:width="match_parent"/>
</DirectionalLayout>
第二步:编写User类和日志工具类
package com.qf.mypreferences;
public class User {
private String name;
private String pswd;
private boolean flag;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPswd() {
return pswd;
}
public void setPswd(String pswd) {
this.pswd = pswd;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", pswd='" + pswd + '\'' +
", flag=" + flag +
'}';
}
}
日志工具类:
package com.qf.preferencedemo.log;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.util.Locale;
public class LogUtils {
private static final String TAG_LOG="preferences";
private static final int DOMAIN_ID = 0xD000F00;
private static final HiLogLabel LOG_LABEL = new HiLogLabel(3,DOMAIN_ID,TAG_LOG);
private static final String LOG_FORMAT="%s: %s";
private LogUtils(){
}
public static void debug(String tag,String msg){
HiLog.debug(LOG_LABEL,String.format(Locale.ROOT,LOG_FORMAT,tag,msg));
}
public static void info(String tag,String msg){
HiLog.info(LOG_LABEL,String.format(Locale.ROOT,LOG_FORMAT,tag,msg));
}
public static void error(String tag,String msg){
HiLog.error(LOG_LABEL,String.format(Locale.ROOT,LOG_FORMAT,tag,msg));
}
}
第三步:编写PreferenceUtils工具类
package com.qf.preferencedemo.utils;
import com.qf.preferencedemo.bean.User;
import ohos.app.Context;
import ohos.data.DatabaseHelper;
import ohos.data.preferences.Preferences;
public class PreferenceUtils {
private Preferences preferences;
private String fileName="login_pref";
private Context context;
public PreferenceUtils(Context context){
this.context = context;
DatabaseHelper helper = new DatabaseHelper(context);
preferences = helper.getPreferences(fileName);
}
/**
*
* @param user
* @return
*/
public boolean saveOrUpdateUser(User user){
preferences.putString("name",user.getName());
preferences.putString("pswd",user.getPswd());
preferences.putBoolean("flag",user.isFlag());
return preferences.flushSync();//将数据从内存保存到文件中,采用同步的方式
}
public User findUser(){
User user = new User();
user.setFlag(preferences.getBoolean("flag",false));
user.setName(preferences.getString("name",""));
user.setPswd(preferences.getString("pswd",""));
return user;
}
public StringBuffer viewPath(){
StringBuffer buffer = new StringBuffer();
//获取数据库保存的路径
buffer.append(context.getPreferencesDir().getAbsolutePath()+"\n")
.append(context.getPreferencesDir().getName()+"\n");
return buffer;
}
}
第四步:编写MainAbilitySlice类测试结果
package com.qf.preferencedemo.slice;
import com.qf.preferencedemo.ResourceTable;
import com.qf.preferencedemo.bean.User;
import com.qf.preferencedemo.log.LogUtils;
import com.qf.preferencedemo.utils.PreferenceUtils;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener {
private Button insert;
private Button update;
private Button viewPath;
private Button search;
private PreferenceUtils utils;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
utils = new PreferenceUtils(getContext());
insert = (Button)findComponentById(ResourceTable.Id_insert);
update = (Button)findComponentById(ResourceTable.Id_update);
viewPath = (Button)findComponentById(ResourceTable.Id_viewpath);
search = (Button)findComponentById(ResourceTable.Id_search);
insert.setClickedListener(this::onClick);
update.setClickedListener(this::onClick);
viewPath.setClickedListener(this::onClick);
search.setClickedListener(this::onClick);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
@Override
public void onClick(Component component) {
switch (component.getId()){
case ResourceTable.Id_insert:
User user = new User();
user.setName("admin");
user.setPswd("root");
user.setFlag(true);
boolean flag = utils.saveOrUpdateUser(user);
if (flag){
LogUtils.info("MainAbilitySlice","保存数据成功!!!!");
}
break;
case ResourceTable.Id_viewpath:
LogUtils.info("MainAbilitySlice","数据库的路径:"+utils.viewPath().toString());
case ResourceTable.Id_search:
LogUtils.info("MainAbilitySlice","查询数据:"+utils.findUser());
break;
case ResourceTable.Id_update:
User update_user = new User();
update_user.setName("jack");
update_user.setPswd("123456");
update_user.setFlag(false);
boolean flag2 = utils.saveOrUpdateUser(update_user);
if (flag2){
LogUtils.info("MainAbilitySlice","修改数据成功!!!!");
}
break;
}
}
}
le.Layout_ability_main);
utils = new PreferenceUtils(getContext());
insert = (Button)findComponentById(ResourceTable.Id_insert);
update = (Button)findComponentById(ResourceTable.Id_update);
viewPath = (Button)findComponentById(ResourceTable.Id_viewpath);
search = (Button)findComponentById(ResourceTable.Id_search);
insert.setClickedListener(this::onClick);
update.setClickedListener(this::onClick);
viewPath.setClickedListener(this::onClick);
search.setClickedListener(this::onClick);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
@Override
public void onClick(Component component) {
switch (component.getId()){
case ResourceTable.Id_insert:
User user = new User();
user.setName("admin");
user.setPswd("root");
user.setFlag(true);
boolean flag = utils.saveOrUpdateUser(user);
if (flag){
LogUtils.info("MainAbilitySlice","保存数据成功!!!!");
}
break;
case ResourceTable.Id_viewpath:
LogUtils.info("MainAbilitySlice","数据库的路径:"+utils.viewPath().toString());
case ResourceTable.Id_search:
LogUtils.info("MainAbilitySlice","查询数据:"+utils.findUser());
break;
case ResourceTable.Id_update:
User update_user = new User();
update_user.setName("jack");
update_user.setPswd("123456");
update_user.setFlag(false);
boolean flag2 = utils.saveOrUpdateUser(update_user);
if (flag2){
LogUtils.info("MainAbilitySlice","修改数据成功!!!!");
}
break;
}
}
}
####