Android 使用SQLite保存数据

Android 使用SQLite保存数据

  • 简介
  • 定义架构和协定
  • 使用 SQL 创建数据库
  • 将信息添加到数据库
    • `insert()`函数介绍
  • 从数据库中读取信息
    • `query()`函数介绍
  • 从数据库中删除信息
    • `delete()`函数介绍
  • 更新数据库
    • `update()`函数介绍
  • 保留数据库连接
  • 调试数据库

简介

对于重复数据或结构化数据(例如联系信息),将数据保存到数据库是理想选择。android.database.sqlite 软件包中提供了在 Android 上使用数据库所需的 API。本篇文章介绍在 Android 上使用 SQLite 数据库。

定义架构和协定

SQL 数据库的主要原则之一是架构,即数据库组织方式的正式声明。架构反映在你用于创建数据库的 SQL 语句中。您可能会发现创建伴随类(称为协定类)很有用,该类以系统化、自记录的方式明确指定了架构的布局。

协定类是定义 URI、表和列名称的常量的容器。通过协定类,您可以在同一软件包的所有其他类中使用相同的常量。这样一来,您就可以在一个位置更改列名称并将其传播到整个代码中。

组织协定类的一种良好方法是将对整个数据库而言具有全局性的定义放入类的根级别。然后,为每个表创建一个内部类。每个内部类都枚举相应表的列。

注意:通过实现 BaseColumns 接口,您的内部类可以继承名为 _ID 的主键字段。

例如,以下协定定义了表示 RSS Feed 的单个表的表名称和列名称:

public final class FeedReaderContract {// To prevent someone from accidentally instantiating the contract class,// make the constructor private.private FeedReaderContract() {}/* Inner class that defines the table contents */public static class FeedEntry implements BaseColumns {// 表名public static final String TABLE_NAME = "entry";// 列名public static final String COLUMN_NAME_TITLE = "title";// 列名public static final String COLUMN_NAME_SUBTITLE = "subtitle";}
}

使用 SQL 创建数据库

定义了数据库的结构后,应实现用于创建和维护数据库和表的方法。以下是用于创建和删除表的一些语句:

// 创建数据库表
private static final String SQL_CREATE_ENTRIES ="CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +FeedEntry._ID + " INTEGER PRIMARY KEY," +FeedEntry.COLUMN_NAME_TITLE + " TEXT," +FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";// 删除数据库表
private static final String SQL_DELETE_ENTRIES ="DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

就像您在设备的内部存储空间中保存文件一样,Android 会将您的数据库存储在您应用的私有文件夹中。您的数据很安全,因为在默认情况下,其他应用或用户无法访问此区域。

SQLiteOpenHelper 类包含一组用于管理数据库的实用 API。当您使用此类获取对数据库的引用时,系统仅在需要时才执行可能需要长时间运行的数据库创建和更新操作,而不是在应用启动期间执行。您仅需调用 getWritableDatabase()getReadableDatabase() 即可。

注意:由于这些操作可能会长时间运行,因此请务必在后台线程中调用 getWritableDatabase()getReadableDatabase()

如需使用 SQLiteOpenHelper,请创建一个用于替换 onCreate()onUpgrade() 回调方法的子类。您可能还需要实现 onDowngrade()onOpen() 方法,但这些方法并非必需。

例如,下面展示了使用上述一些命令的 SQLiteOpenHelper 实现:

public class FeedReaderDbHelper extends SQLiteOpenHelper {// If you change the database schema, you must increment the database version.public static final int DATABASE_VERSION = 1;public static final String DATABASE_NAME = "FeedReader.db";private static final String SQL_CREATE_ENTRIES ="CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +FeedEntry._ID + " INTEGER PRIMARY KEY," +FeedEntry.COLUMN_NAME_TITLE + " TEXT," +FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";private static final String SQL_DELETE_ENTRIES ="DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;public FeedReaderDbHelper(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);}public void onCreate(SQLiteDatabase db) {// 创建数据库表db.execSQL(SQL_CREATE_ENTRIES);}public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// This database is only a cache for online data, so its upgrade policy is// to simply to discard the data and start overdb.execSQL(SQL_DELETE_ENTRIES);onCreate(db);}public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {onUpgrade(db, oldVersion, newVersion);}
}

如需访问您的数据库,请实例化 SQLiteOpenHelper 的子类:

FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());

将信息添加到数据库

通过将 ContentValues 对象传递给 insert() 方法,将数据插入到数据库中:

// Gets the data repository in write mode
SQLiteDatabase db = dbHelper.getWritableDatabase();// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);// Insert the new row, returning the primary key value of the new row
long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);

insert()函数介绍

public long insert (String table, String nullColumnHack, ContentValues values)
ParametersDetails
tableString: 要插入行的表名,该值不能为空
nullColumnHackString: 可选;可为null。SQL 不允许在未命名至少一个列名的情况下插入完全为空的记录。如果提供的值为空,则不知道列名,无法插入空行。如果不设置为空,nullColumnHack 参数会提供可空的列名,以便在值为空的情况下明确插入 NULL。
valuesContentValues: 该映射包含该行的初始列值。键应该是列名,值应该是列值。该值可以为空。
ReturnsDetails
long新插入行的行 ID,如果发生错误则为 -1

返回新创建行的 ID;如果在插入数据时出错,会返回 -1。如果您的数据与数据库中已有的数据之间存在冲突,就会出现这种情况。

从数据库中读取信息

如需从数据库中读取信息,请使用 query() 方法,向其传递您的选择条件和所需的列。该方法合并了 insert()update() 元素,不过列表定义了要提取的数据(“预测值”),而不是要插入的数据。查询结果会包含在 Cursor 对象中返回给您。

SQLiteDatabase db = dbHelper.getReadableDatabase();// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {BaseColumns._ID,FeedEntry.COLUMN_NAME_TITLE,FeedEntry.COLUMN_NAME_SUBTITLE};// Filter results WHERE "title" = 'My Title'
String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
String[] selectionArgs = { "My Title" };// How you want the results sorted in the resulting Cursor
String sortOrder =FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";Cursor cursor = db.query(FeedEntry.TABLE_NAME,   // The table to queryprojection,             // The array of columns to return (pass null to get all)selection,              // The columns for the WHERE clauseselectionArgs,          // The values for the WHERE clausenull,                   // don't group the rowsnull,                   // don't filter by row groupssortOrder               // The sort order);

query()函数介绍

public Cursor query (String table, 	// 要查询的表名,不能为空String[] columns, 	// 要返回的列的列表。传递 null 将返回所有列String selection, 	// 声明要返回哪些行的过滤器,传递 null 将返回给定表的所有行String[] selectionArgs,  // 可以在选择中包含 ?,它将被选择参数中的值替换,按照它们在选择中出现的顺序String groupBy, 	// 分组过滤器,传递 null 将导致行不被分组String having, 		// 如果使用行分组,则过滤器声明游标中包含哪些行组,传递 null 将导致包含所有行组String orderBy)		// 对行进行排序,传递 null 将使用默认排序顺序,该顺序可能是无序的

第三个参数和第四个参数(selectionselectionArgs)会合并起来创建一个 WHERE 子句。由于这两个参数是与选择查询分开提供的,因此它们在合并之前会进行转义。这样一来,您的选择语句就不受 SQL 注入的影响。

如需查看光标中的某一行,请使用 Cursor move 方法之一,您必须始终在开始读取值之前调用该方法。由于光标从位置 -1 开始,因此调用 moveToNext() 会将“读取位置”置于结果的第一个条目上,并返回光标是否已经过结果集中的最后一个条目。对于每一行,您都可以通过调用 Cursor get 方法之一(例如 getString()getLong())读取列的值。对于每个 get 方法,您必须传递所需列的索引位置,您可以通过调用 getColumnIndex()getColumnIndexOrThrow() 获取该位置。遍历结果之后,请对光标调用 close() 以释放其资源。例如,以下代码展示了如何获取存储在光标中的所有项目 ID 并将其添加到列表中:

List itemIds = new ArrayList<>();
while(cursor.moveToNext()) {long itemId = cursor.getLong(cursor.getColumnIndexOrThrow(FeedEntry._ID));itemIds.add(itemId);
}
cursor.close();

从数据库中删除信息

如需从表中删除行,您需要提供选择条件,以标识 delete() 方法的目标行。该机制与 query() 方法的目标选择参数的工作方式相同。它将选择规范划分为选择子句和选择参数。子句定义要查看的列,并允许您合并列测试。参数是要测试的值,这些值绑定到子句中。由于结果的处理方式与常规 SQL 语句的处理方式不同,因此不受 SQL 注入的影响。

// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { "MyTitle" };
// Issue SQL statement.
int deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);

delete() 方法的返回值表示从数据库中删除的行数。

delete()函数介绍

// return: 如果传入 whereClause,则影响的行数,否则为 0。要删除所有行并获取计数,请传递“1”作为 whereClause。
public int delete (String table, 		// 要操作的表名,不能为空String whereClause, 	// 删除时应用的可选 WHERE 子句。传递 null 将删除所有行。String[] whereArgs)		// 可以在 where 子句中包含 ?s,它将被 whereArgs 中的值替换。这些值将绑定为字符串。如果 whereClause 为 null 或不包含 ?s,则 whereArgs 可能为 null。

更新数据库

如果您需要修改数据库值的子集,请使用 update() 方法。

SQLiteDatabase db = dbHelper.getWritableDatabase();// New value for one column
String title = "MyNewTitle";
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);// Which row to update, based on the title
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
String[] selectionArgs = { "MyOldTitle" };int count = db.update(FeedReaderDbHelper.FeedEntry.TABLE_NAME,values,selection,selectionArgs);

update() 方法的返回值是数据库中受影响的行数。

update()函数介绍

public int update (String table, 			// 要更新的表名,不能为nullContentValues values, 		// 从列名到新列值的映射。 null 是一个有效值,将被转换为 NULL。String whereClause, 		// 更新时应用的可选 WHERE 子句。传递 null 将更新所有行。String[] whereArgs)			// 可以在 where 子句中包含 ?s,它将被 whereArgs 中的值替换。这些值将绑定为字符串。如果 whereClause 为 null 或不包含 ?s,则 whereArgs 可能为 null。

保留数据库连接

由于在数据库关闭时,调用 getWritableDatabase()getReadableDatabase() 的成本比较高,因此只要您有可能需要访问数据库,就应保持数据库连接处于打开状态。通常情况下,最好在发出调用的 Activity 的 onDestroy() 中关闭数据库。

@Override
protected void onDestroy() {dbHelper.close();super.onDestroy();
}

调试数据库

Android SDK 提供一个 sqlite3 shell 工具,您可以使用该工具浏览表内容、运行 SQL 命令以及在 SQLite 数据库中执行其他实用操作。

sqlite3 可启动用于检查 SQLite 数据库的 sqlite 命令行程序。它包含用于输出表格内容的 .dump 以及用于输出现有表格的 SQL CREATE 语句的 .schema 等命令。您也可以从命令行执行 SQLite 命令,如下所示:

$ adb -s emulator-5554 shell
$ sqlite3 /data/data/com.example.app/databases/rssitems.db
SQLite version 3.3.12
Enter ".help" for instructions

注意:您必须拥有对文件系统的 root 权限(例如在模拟器上),才能访问 SQLite 数据库。

如需了解详情,请参阅 sqlite3 命令行文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/779780.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云服务器8核32G配置租用优惠价格94元/月、1362元一年

8核32G云服务器京东云轻量云主机价格94元1个月、282元3个月、673元6个月、1362元一年&#xff0c;配置8C32G-100G SSD系统盘-10M带宽-2000G月流量 华北-北京&#xff0c;京东云优惠活动 yunfuwuqiba.com/go/jd 活动链接打开如下图&#xff1a; 8核32G云服务器京东云轻量云主机价…

数据挖掘篇【 alias方法 和 隐式转换 】

目录 介绍 隐式转换 alias方法 介绍 在 Apache Spark 中&#xff0c;.alias 是一个方法&#xff0c;用于给 DataFrame 的列或表达式指定一个新的别名。当你需要对列进行重命名或者在 SQL 表达式中使用更易读的名称时&#xff0c;这个方法非常有用。 .alias 方法通常与 Dat…

CSS(三)---【盒子模型、边框、外边距合并】

零.前言 本篇主要介绍CSS中最重要的一种概念模型&#xff1a;“盒子模型”。 关于CSS的更多内容&#xff0c;可以查看作者之前的文章&#xff1a; CSS(一)---【CSS简介、导入方式、八种选择器、优先级】-CSDN博客 CSS(二)---【常见属性、复合属性使用】-CSDN博客 一.盒子模…

WebCopilot:一款功能强大的子域名枚举和安全漏洞扫描工具

关于WebCopilot WebCopilot是一款功能强大的子域名枚举和安全漏洞扫描工具&#xff0c;该工具能够枚举目标域名下的子域名&#xff0c;并使用不同的开源工具检测目标存在的安全漏洞。 工具运行机制 WebCopilot首先会使用assetsfinder、submaster、subfinder、accumt、finddom…

Springboot 后端文件下载:记一次根据地址连接下载文件并且返回给前端(避免跨域问题)

目的&#xff1a;为了解决前端直接根据文件连接地址下载导致的跨域问题&#xff1b;后端提供一个接口&#xff0c;根据前端传入的文件地址链接下载文件到本地再返回文件流给前端&#xff0c;从而避免跨域问题&#xff08;代码已上线&#xff09; controller层代码&#xff1a;…

Jamba:AI21 实验室发布首个应用级的 Mamba 架构 AI 模型

AI21 实验室发布了 Jamba&#xff0c;这是全球首个基于 Mamba 架构的、可用于实际应用的 AI 模型。目前大多数模型&#xff08;例如 GPT、Gemini 和 Llama&#xff09;都基于 Transformer 架构。Jamba 结合了 Mamba 结构化状态空间模型 (SSM) 和传统 Transformer 架构的优点&am…

属性选择器

1.[title]{background:yellow;}&#xff1a;所有带title标签设置成黄色 2.div[class]{background:yellow;}&#xff1a;所有div中带class标签设置成黄色 3.div[classbox1]{border:1px solid blue; }&#xff1a;div中包含class并且classbox1的设置成蓝边框 4. class…

Postman Tests设置Global读取不是最新值,跟Tests执行顺序有关(踩坑笔记)

前言 在执行Run Collection的时候&#xff0c;发现设置的全局变量每次读取都是旧值&#xff0c;没有读取到最新的值。 背景 有2个地方需要动态参数&#xff0c;一个URL&#xff0c;一个Body&#xff0c;因此需要设置Tests脚本。 url动态参数 url&#xff1a;动态参数projec…

【Java】打包:JAR、EAR、WAR

打包&#xff1a;JAR、EAR、WAR war 是一个 Web 模块&#xff0c;其中需要包括 WEB-INF&#xff0c;是可以直接运行的 WEB 模块。而 jar 一般只是包括一些 class 文件&#xff0c;在声明了 main_class 之后是可以用 java 命令运行的。 它们都是压缩的包&#xff0c;拿 Tomcat …

《Linux 源码趣读》的读书笔记-第1部分

事情起源 工作快7年了,由于我不断地完成各种奇怪的功能,我终于能看懂计算机组成和操作系统了。 当我终于看一个东西不再是天书了,那一刻,我是上头的。 于是我简单刷了几个视频:《王道-计算机组成原理》、《王道-操作系统》、《王道-计算机网络》。 刷完我总觉得哪里不…

【基于springboot分析Quartz(v2.3.2)的启动流程】

基于springboot分析Quartz&#xff08;v2.3.2&#xff09;的启动流程 最近公司的定时任务使用了Quartz框架&#xff0c;在开发中经常出现定任务不执行了的问题&#xff0c;但是我又找不到原因所在&#xff0c;可把我愁坏了。于是我决定看看Quartz框架是怎么调度任务的。&#x…

C# wpf 实现底部嵌入HwndHost

WPF Hwnd窗口互操作系列 第一章 嵌入Hwnd窗口 第二章 嵌入WinForm控件 第三章 嵌入WPF控件 第四章 底部嵌入HwndHost&#xff08;本章&#xff09; 文章目录 WPF Hwnd窗口互操作系列前言一、如何实现&#xff1f;1、底部创建窗口&#xff08;1&#xff09;、创建透明窗口&…

FebHost:什么是哈萨克斯坦.KZ域名?

哈萨克斯坦&#xff0c;作为中亚地区重要的一员,其国家域名”.kz”正成为这个独立国家在网络世界中的代表。作为一个经济快速发展的国家,哈萨克斯坦的互联网基础设施和网络应用也在蓬勃发展。而.kz域名正是哈萨克斯坦网络身份的重要体现。 作为注册和管理.kz域名的主要机构,哈…

2020年30米二级分类北京市土地利用数据

引言 北京市省土地利用数据产品是指基于Landsat TM/ETM/OLI遥感影像&#xff0c;采用遥感信息提取方法&#xff0c;并结合野外实测&#xff0c;以及参照国内外现有的土地利用/土地覆盖分类体系&#xff0c;经过波段选择及融合&#xff0c;图像几何校正及配准并对图像进行增强处…

Elasticsearch 开放 inference API 增加了对 Cohere Embeddings 的支持

作者&#xff1a;来自 Elastic Serena Chou, Jonathan Buttner, Dave Kyle 我们很高兴地宣布 Elasticsearch 现在支持 Cohere 嵌入&#xff01; 发布此功能是与 Cohere 团队合作的一次伟大旅程&#xff0c;未来还会有更多合作。 Cohere 是生成式 AI 领域令人兴奋的创新者&…

SpringBoot+Prometheus+Grafana实现应用监控和报警

一、背景 SpringBoot的应用监控方案比较多&#xff0c;SpringBootPrometheusGrafana是目前比较常用的方案之一。它们三者之间的关系大概如下图&#xff1a; 关系图 二、开发SpringBoot应用 首先&#xff0c;创建一个SpringBoot项目&#xff0c;pom文件如下&#xff1a; <…

蓝桥杯嵌入式老竞赛板在MDK5上使用CooCox下载出现unknown device的问题

本文是在参考网上博客并经过实操解决自己遇到的问题总结而成&#xff0c;只是为了让后来者少走弯路。 本文是在在LED闪烁实验时遇到这个问题 蓝桥杯嵌入式老竞赛板在MDK5上使用CooCox下载出现unknown device的问题 环境&#xff1a;win11系统&#xff0c;keil MDK 518 老竞赛…

【Unity】TextMeshPro富文本

启用富文本 在Unity里&#xff0c;如果需要使用富文本&#xff0c;首先需要开启Rich Text 如果不开启Rich Text&#xff0c;就会在UI上显示富文本代码 1.粗体 <b>Game</b> Over2.斜体 <i>Game</i> Over3.下划线 <u>Game</u> Over4…

.Net 知识杂记

记录平日中琐碎的.net 知识点。不定期更新 目标框架名称(TFM) 我们创建C#应用程序时&#xff0c;在项目的工程文件(*.csproj)中都有targetFramework标签&#xff0c;以表示项目使用的目标框架 各种版本的TFM .NET Framework .NET Standard .NET5 及更高版本 UMP等 参考文档&a…

opencv-python库 cv2.imread() 读取图片

cv2.imread函数是OpenCV库中用于读取图像文件的函数。以下是该函数的详细使用方法&#xff1a; 函数语法&#xff1a; cv2.imread(filename, flags1)参数解释&#xff1a; filename&#xff1a;字符串类型&#xff0c;指定要读取的图像文件的相对地址或完整路径。 flags&…