Android官方开发文档Training系列课程中文版:数据存储之数据库存储

简介: 原文地址:http://android.xsoftlab.net/training/basics/data-storage/databases.html对于保存重复的结构化的数据最理想的方式就是存到数据库,比如联系人信息。

原文地址:http://android.xsoftlab.net/training/basics/data-storage/databases.html

对于保存重复的结构化的数据最理想的方式就是存到数据库,比如联系人信息。这节课假定你有SQL数据库基础,会帮助你开始学习Android上的SQLite数据库。你将通过使用android.database.sqlite包下API来操作数据库。

定义架构以及契约

SQL数据库的主要原则之一就是架构:一份数据库组织结构的正式声明。架构反映在SQL语句中,你可以使用它来创建数据库。你可能会发现它对于创建一个辅助类来说是非常有帮助的。我们所已知的contract类,它明确的指明了在系统化和自我记录化的方式下的布局模式。

contract类是一个契约容器,它定义了URI、TABLE、COLUMN的名称。contract类允许你使用相同的契约访问处于同一个包下的所有类。它可以使你在一个地方改了列名,然后让这个更改消息在代码中传播(Tips:这里应该说的是观察者模式)。

组织一个contract类的最好方式就是在类的根级中定义全局的数据库。然后对每一个表都创建一个内部类并且在内部类中列举出它的每一列列名。

Note:内部类可以通过实现BaseColumns接口,继承一个被称为_ID的主键,一些Android中的类比如cursor adapter会期望拥有这个主键,不过这不是必须的,但是它可以有助于数据库在Android框架层中运行的更协调。

下面的小片段定义了一个单表,并列举出了它表名和列名:

public final class FeedReaderContract {
    // To prevent someone from accidentally instantiating the contract class,
    // give it an empty constructor.
    public FeedReaderContract() {}
    /* Inner class that defines the table contents */
    public static abstract class FeedEntry implements BaseColumns {
        public static final String TABLE_NAME = "entry";
        public static final String COLUMN_NAME_ENTRY_ID = "entryid";
        public static final String COLUMN_NAME_TITLE = "title";
        public static final String COLUMN_NAME_SUBTITLE = "subtitle";
        ...
    }
}

使用SQL Helper创建一个数据库

一旦定义了数据库的模型,你应该实现可以创建并可以为维护数据库以及表的方法。下面是创建表、删除表的一些典型声明:

private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
    FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
    FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
    ... // Any other options for the CREATE command
    " )";
private static final String SQL_DELETE_ENTRIES =
    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

就像保存在内部存储器上的文件,Android会将你的数据库文件保存到与应用程序有关的私有磁盘空间内。所以你的数据是安全的,因为默认情况下这块区域是不可以被其它应用程序访问的。

SQLiteOpenHelper类中有一系列非常有用的API。当使用这个类的API操作数据库的引用时,系统会潜在的执行耗时操作。所以创建和更新数据库的时间,应该放在需要操作的时候操作,而不是在应用启动的时候操作。你需要操作的所有事情需要通过调用getWritableDatabase()或者getReadableDatabase()来完成。

Note:因为这些操作可能会长时间运行,所以要确保调用getWritableDatabase()或getReadableDatabase()的线程是工作线程,比如使用AsyncTask或者IntentService

为了使用SQLiteOpenHelper,需要创建SQLiteOpenHelper的一个子类然后重写onCreate(), onUpgrade()和onOpen()回调方法。你可能还需要实现onDowngrade(),但在这里不是必须的。
下面是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";
    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 over
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }
}

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

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

放置信息到数据库

通过往insert()方法中传递ContentValues对象可以向数据库中插入数据:

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

insert()方法的第一个参数是一个简单的表名,第二个参数提供了当ContentValues为空的时候framework插入NULL值的那一列的列名(如果你想设置这个参数为”null”,那么当插入的值为空的时候framework将不会空值插入到数据库)。

从数据库中读取信息

为了可以从数据库读取数据,应该使用query()方法,传入你的选择条件以及期望返回的那几列。这个方法结合了insert()方法和update()方法的基础,定义了你想匹配的那几列,但并不插入数据。查询返回的结果会在Cursor对象中。

SQLiteDatabase db = mDbHelper.getReadableDatabase();
// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    FeedEntry._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_UPDATED,
    ...
    };
// How you want the results sorted in the resulting Cursor
String sortOrder =
    FeedEntry.COLUMN_NAME_UPDATED + " DESC";
Cursor c = db.query(
    FeedEntry.TABLE_NAME,  // The table to query
    projection,                               // The columns to return
    selection,                                // The columns for the WHERE clause
    selectionArgs,                            // The values for the WHERE clause
    null,                                     // don't group the rows
    null,                                     // don't filter by row groups
    sortOrder                                 // The sort order
    );

为了查看cursor中的每一行,需要使用Cursor的移动方法,在你开始读取数据之前你需要一直调用这个方法。通常情况下,你应该在最开始调用moveToFirst()方法,它会将读取位置放置到第一个结果实体所在的位置。对于每一行来说,你可以通过调用Cursor类中的其中一个方法读取一个列值,比如使用getString(),getLong()。对于每一个get方法,你必须给这些方法传递一个要读取那一列的索引位置值,你可以通过getColumnIndex()或者getColumnIndexOrThrow()获得索引位置值:

cursor.moveToFirst();
long itemId = cursor.getLong(
    cursor.getColumnIndexOrThrow(FeedEntry._ID)
);

从数据库中删除信息

如果要从数据库表中删除一行,你需要提供可以标识那一行的选择条件。数据库API提供了一种机制,用于创建选择条件,这种机制可以防止被SQL注入。这种机制会将选择规范划分为选择条件和选择参数。选择条件用于定义查找的列,也允许同时查找多列。选择参数的值会测试绑定到条件上的对应值。因为结果不会像常规的SQL语句那样被处理,它会用于免疫SQL注入的。

// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { String.valueOf(rowId) };
// Issue SQL statement.
db.delete(table_name, selection, selectionArgs);

更新数据库

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

更新表的方法结合了insert()的内容语法与delete()的where语法。

SQLiteDatabase db = mDbHelper.getReadableDatabase();
// New value for one column
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
// Which row to update, based on the ID
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(rowId) };
int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);
目录
相关文章
|
4月前
|
存储 关系型数据库 数据库
附部署代码|云数据库RDS 全托管 Supabase服务:小白轻松搞定开发AI应用
本文通过一个 Agentic RAG 应用的完整构建流程,展示了如何借助 RDS Supabase 快速搭建具备知识处理与智能决策能力的 AI 应用,展示从数据准备到应用部署的全流程,相较于传统开发模式效率大幅提升。
附部署代码|云数据库RDS 全托管 Supabase服务:小白轻松搞定开发AI应用
|
2月前
|
存储 JSON 数据建模
鸿蒙 HarmonyOS NEXT端云一体化开发-云数据库篇
云数据库采用存储区、对象类型、对象三级结构,支持灵活的数据建模与权限管理,可通过AGC平台或本地项目初始化,实现数据的增删改查及端侧高效调用。
143 1
|
4月前
|
存储 SQL 前端开发
跟老卫学HarmonyOS开发:ArkTS关系型数据库开发
本节以“账本”为例,使用关系型数据库接口实现账单的增、删、改、查操作。通过创建ArkTSRdb应用,演示如何操作RdbStore进行数据管理,并结合界面按钮实现交互功能。
183 0
跟老卫学HarmonyOS开发:ArkTS关系型数据库开发
|
6月前
|
存储 缓存 自然语言处理
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
169 8
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
|
7月前
|
SQL 调度 数据库
开发YashanDB数据库?用 DBeaver for YashanDB 更顺手
数据库开发复杂易错,尤其在企业级场景中。为提升效率,YashanDB 团队基于 DBeaver 开源工具打造专属解决方案——DBeaver for YashanDB。它支持多类型数据库对象管理(表、视图、函数等),适配 YashanDB 特有表结构(HEAP、LSC),提供智能补全、语法高亮、SQL 调试等功能,让开发更高效流畅。推荐用于数据库应用开发团队、高频调试用户及中大型企业统一工具栈场景。
|
7月前
|
SQL 数据可视化 IDE
开发数据库不想写命令?YashanDB Developer Center 帮你轻松搞定
YashanDB Developer Center(YDC)是一款可视化的数据库开发工具,专为提升数据库开发效率而设计。它通过图形化对象管理让数据库对象清晰可见,提供智能SQL编辑器支持语法高亮与自动补全,实现PL调试的图形化操作,帮助快速定位问题。此外,操作记录可追溯,多端灵活部署,适配多种场景。无论是中大型企业研发团队,还是不熟悉命令行的业务开发者,YDC都能显著优化开发体验,堪称YashanDB的“可视化IDE”。
|
10月前
|
前端开发 Java 数据库连接
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
432 2
|
11月前
|
SQL 关系型数据库 API
HarmonyOs开发:关系型数据库封装之增删改查
每个方法都预留了多种调用方式,比如使用callback异步回调或者使用Promise异步回调,亦或者同步执行,大家在使用的过程中,可以根据自身业务需要进行选择性调用,也分别暴露了成功和失败的方法,可以针对性的判断在执行的过程中是否执行成功。
376 13
|
11月前
|
存储 前端开发 关系型数据库
鸿蒙开发:实现键值数据库存储
对于数据量比较的小的,我们直接选择轻量级的用户首选项方式即可,而对于数据量比较大的情况下,直接可以使用数据库,而对于相对来说,比较大的数据,我们就可以使用键值型数据库方式
300 2

热门文章

最新文章