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);
目录
相关文章
|
3月前
|
存储 人工智能 Cloud Native
云栖重磅|从数据到智能:Data+AI驱动的云原生数据库
在9月20日2024云栖大会上,阿里云智能集团副总裁,数据库产品事业部负责人,ACM、CCF、IEEE会士(Fellow)李飞飞发表《从数据到智能:Data+AI驱动的云原生数据库》主题演讲。他表示,数据是生成式AI的核心资产,大模型时代的数据管理系统需具备多模处理和实时分析能力。阿里云瑶池将数据+AI全面融合,构建一站式多模数据管理平台,以数据驱动决策与创新,为用户提供像“搭积木”一样易用、好用、高可用的使用体验。
云栖重磅|从数据到智能:Data+AI驱动的云原生数据库
|
1月前
|
数据采集 数据库 Python
有哪些方法可以验证用户输入数据的格式是否符合数据库的要求?
有哪些方法可以验证用户输入数据的格式是否符合数据库的要求?
144 75
|
3月前
|
存储 监控 数据处理
flink 向doris 数据库写入数据时出现背压如何排查?
本文介绍了如何确定和解决Flink任务向Doris数据库写入数据时遇到的背压问题。首先通过Flink Web UI和性能指标监控识别背压,然后从Doris数据库性能、网络连接稳定性、Flink任务数据处理逻辑及资源配置等方面排查原因,并通过分析相关日志进一步定位问题。
282 61
|
2月前
|
SQL 存储 运维
从建模到运维:联犀如何完美融入时序数据库 TDengine 实现物联网数据流畅管理
本篇文章是“2024,我想和 TDengine 谈谈”征文活动的三等奖作品。文章从一个具体的业务场景出发,分析了企业在面对海量时序数据时的挑战,并提出了利用 TDengine 高效处理和存储数据的方法,帮助企业解决在数据采集、存储、分析等方面的痛点。通过这篇文章,作者不仅展示了自己对数据处理技术的理解,还进一步阐释了时序数据库在行业中的潜力与应用价值,为读者提供了很多实际的操作思路和技术选型的参考。
53 1
|
2月前
|
存储 Java easyexcel
招行面试:100万级别数据的Excel,如何秒级导入到数据库?
本文由40岁老架构师尼恩撰写,分享了应对招商银行Java后端面试绝命12题的经验。文章详细介绍了如何通过系统化准备,在面试中展示强大的技术实力。针对百万级数据的Excel导入难题,尼恩推荐使用阿里巴巴开源的EasyExcel框架,并结合高性能分片读取、Disruptor队列缓冲和高并发批量写入的架构方案,实现高效的数据处理。此外,文章还提供了完整的代码示例和配置说明,帮助读者快速掌握相关技能。建议读者参考《尼恩Java面试宝典PDF》进行系统化刷题,提升面试竞争力。关注公众号【技术自由圈】可获取更多技术资源和指导。
|
2月前
|
前端开发 JavaScript 数据库
获取数据库中字段的数据作为下拉框选项
获取数据库中字段的数据作为下拉框选项
66 5
|
2月前
|
存储 数据采集 数据挖掘
CSV vs 数据库:数据存储的最佳选择是什么
本文介绍了爬虫数据存储中CSV和数据库的优缺点,分析了两者在不同场景下的适用性。CSV简单易用、资源消耗低,适合小量数据;数据库则在处理大量数据和复杂查询时表现出色,支持并发操作。通过Python代码示例,展示了如何使用多线程和爬虫代理IP技术将百度搜索数据存储到MySQL数据库中,适用于大型项目和复杂数据分析需求。
156 2
|
3月前
|
SQL 关系型数据库 数据库
国产数据实战之docker部署MyWebSQL数据库管理工具
【10月更文挑战第23天】国产数据实战之docker部署MyWebSQL数据库管理工具
278 4
国产数据实战之docker部署MyWebSQL数据库管理工具
|
3月前
|
关系型数据库 MySQL 数据库
GBase 数据库如何像MYSQL一样存放多行数据
GBase 数据库如何像MYSQL一样存放多行数据
|
3月前
|
关系型数据库 分布式数据库 数据库
云栖大会|从数据到决策:AI时代数据库如何实现高效数据管理?
在2024云栖大会「海量数据的高效存储与管理」专场,阿里云瑶池讲师团携手AMD、FunPlus、太美医疗科技、中石化、平安科技以及小赢科技、迅雷集团的资深技术专家深入分享了阿里云在OLTP方向的最新技术进展和行业最佳实践。

热门文章

最新文章

  • 1
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    23
  • 2
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    21
  • 3
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    52
  • 4
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    35
  • 5
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    70
  • 6
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    111
  • 7
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
    29
  • 8
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
    263
  • 9
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
    75
  • 10
    【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
    36