【Android应用开发技术:数据存储】数据库

原创 2015年07月30日 16:40:25

作者:郭孝星
微博:郭孝星的新浪微博
邮箱:allenwells@163.com
博客:http://blog.csdn.net/allenwells
Github:https://github.com/AllenWells

【Android应用开发技术:数据存储】章节列表

SQL中一个中重要的概念是schema。

Schema是一种DB结构的正式声明。schema是从你创建DB的SQL语句中生成的。

一般情况下,创建一个伴随类(companion class)是很有益的,这个类称为合约类(contract class),它用一种系统化并且自动生成文档的方式,显示指定了Schema样式。

Contract Clsss是一些常量的容器。它定义了例如URIs,表名,列名等。这个Contract类允许同一个包下与其他类使用同样的常量。它让我们只需要在一个地方修改列名,然后这个列名就可以自动传递给整个代码。

一 定义DB结构

组织Contract类的好方法是在你的类的根层级定义一些全局变量,然后为每一个table来创建内部类,如下所示:

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";
        ...
    }
}

通过实现BaseColumns的接口,内部类可以继承到一个名为_ID的主键,这个对于Android里面的一些类似Cursor Adaptor类是很有必要的。这么做不是必须的,但这样能够使得DB与Android的Framework能够很好的相容。

二 创建DB

当定义好了DB的结构之后,我们实现那些创建与维护DB与table的方法。下面是一些典型的创建与删除table的语句。

private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedReaderContract.FeedEntry.TABLE_NAME + " (" +
    FeedReaderContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
    FeedReaderContract.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 " + TABLE_NAME_ENTRIES;

就像保存文件到设备的internal storage 一样,Android会保存db到程序的private的空间上。数据是受保护的,因为那些区域默认是私有的,不可被其他程序所访问。

在SQLiteOpenHelper类中有一些很有用的APIs。当使用这个类来做一些与你的db有关的操作时,系统会对那些有可能比较耗时的操作(例如创建与更新等)在真正需要的时候才去执行,而不是在app刚启动的时候就去做那些动作。我们需要做的仅仅是执行getWritableDatabase()或者getReadableDatabase(),因为那些操作可能是很耗时的,所以我们需要在background thread(AsyncTask or IntentService)里面去执行getWritableDatabase()或者getReadableDatabase()。

为了使用SQLiteOpenHelper, 我们需要创建一个子类并重写onCreate(), onUpgrade()与onOpen()等callback方法。有时候还需要实现onDowngrade(), 但是这并不是必需的,如下所示:

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());

三 DB数据操作

3.1 数据添加

使用insert()方法进行信息的添加, insert()方法的第一个参数是table名,第二个参数会使得系统自动对那些ContentValues没有提供数据的列填充数据为null ,如果第二个参数传递的是null,那么系统则不会对那些没有提供数据的列进行填充,如下所示:

// 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(FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_CONTENT, content);
// Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
         FeedReaderContract.FeedEntry.TABLE_NAME,
         FeedReaderContract.FeedEntry.COLUMN_NAME_NULLABLE,
         values);

3.2 数据读取

使用query()方法进行数据读取,传递你需要查询的条件。查询后会返回一个Cursor对象。

SQLiteDatabase db = mDbHelper.getReadableDatabase();
// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    FeedReaderContract.FeedEntry._ID,
    FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE,
    FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED,
    ...
    };
// How you want the results sorted in the resulting Cursor
String sortOrder =
    FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED + " DESC";
Cursor c = db.query(
    FeedReaderContract.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的其中一个move方法,但必须在读取值之前调用。一般来说,应该先调用moveToFirst()函数,将读取位置置于结果集最开始的位置。对每一行,我们可以使用cursor的其中一个get方法比如getString()或getLong()获取列的值。对于每一个get方法必须传递想要获取的列的索引位置(index position),索引位置可以通过调用getColumnIndex()或getColumnIndexOrThrow()获得。

从course对象中读取数据信息,如下所示:

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

3.3 数据删除

和查询信息一样,删除数据同样需要提供一些删除标准。DB的API提供了一个防止SQL注入的机制来创建查询与删除标准。

SQL Injection:随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多。但是由于程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入。

这个机制把查询语句划分为选项条款与选项参数两部分。条款部分定义了查询的列的特征,参数部分用来测试是否符合前面的条款。(Android官方原文:The clause defines the columns to look at, and also allows you to combine column tests. The arguments are values to test against that are bound into the clause.) 因为处理的结果与通常的SQL语句不同,这样可以避免SQL注入问题。

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

1.4 数据更新

当你需要修改DB中的某些数据时,使用update()方法,更新操作结合了删除和插入的语法,如下所示:

SQLiteDatabase db = mDbHelper.getReadableDatabase();
// New value for one column
ContentValues values = new ContentValues();
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
// Which row to update, based on the ID
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(rowId) };
int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);

【Android应用开发技术:数据存储】SharedPreferences

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:allenwells@163.com 博客:http://blog.csdn.net/allenwells Github:https://githu...
  • AllenWells
  • AllenWells
  • 2015年07月30日 14:24
  • 641

数据存储全方案,详解持久化技术

任何一个应用程序其实说白了就是在不停地和数据打交道,我们聊 QQ、看新闻、刷微博所关心的都是里面的数据,没有数据的应用程序就变成了一个空壳子,对用户来说没有任何实际用途。那么这些数据都是从哪来的呢?现...
  • u013678930
  • u013678930
  • 2016年03月10日 14:57
  • 2358

网络爬虫开发技术——数据存储以及多线程

0×00 介绍 本文我们就两个方面来讨论如何改进我们的爬虫:数据存储和多线程,当然我承认这是为我们以后要讨论的一些东西做铺垫。 目的:通常我们需要对爬虫捕捉的数据进行分析,处理,再次利用或者格式化,显...
  • zhangshangui_2015
  • zhangshangui_2015
  • 2016年04月06日 13:41
  • 2280

【Android应用开发技术:数据存储】章节列表

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:allenwells@163.com 博客:http://blog.csdn.net/allenwells Github:https://githu...
  • AllenWells
  • AllenWells
  • 2015年08月06日 14:15
  • 456

Android应用开发揭秘[高清PDF版+源码]

编辑推荐 本书内容全面,不仅详细讲解了Android框架、Android组件等基础知识,而且还深入阐述了传感器、语音识别、桌面组件开发等高级知识,最重要的是还全面介绍了如何利用原生的C/C++(...
  • xtd412
  • xtd412
  • 2016年07月29日 17:24
  • 592

GIS海量数据的存储和读取

虽然现在硬件发展很快,内存、CPU等硬件指标都有很大的提高,但要PC机上处理GIS海量数据,还是显得比较吃力,而核心问题就在GIS数据的存储和读取上,这里主要涉及到三个子问题:1,图元是按怎样的顺序存...
  • lggrief
  • lggrief
  • 2016年10月12日 13:51
  • 1202

云存储数据的一般完整性验证

最近毕设做的是云存储数据的完整性研究,因而现在对这里有了一点了解,现在把自己的理解写下来,也是为了加深理解吧! 数据的完整性验证是指验证收到的数据和原来的数据是否保持完全一致的证明手段成为完整性验证...
  • yangfazhi3014
  • yangfazhi3014
  • 2015年05月15日 12:12
  • 1111

Android应用开发详解

本文内容,主题是透过应用程序来分析Android系统的设计原理与构架。我们先会简单介绍一下Android里的应用程序编程,然后以这些应用程 序在运行环境上的需求来分析出,为什么我们的Android系统...
  • permike
  • permike
  • 2014年12月05日 21:35
  • 3830

软件综合实习——基于NoSQL数据库的空间数据存储

前言:在“软件综合实习”这门实践课中选择了一个题目《基于NoSQL数据库的空间数据存储》,基于HBase的空间数据存储和查询。此博客是这次课程设计过程中的简单记录。...
  • u013390476
  • u013390476
  • 2016年09月30日 09:39
  • 848

Android应用开发-数据存储和界面展现

测试 黑盒测试 测试逻辑业务 白盒测试 测试逻辑方法 根据测试粒度 方法测试:function test单元测试:unit test集成测试:integration...
  • liuzhupeng
  • liuzhupeng
  • 2017年05月24日 11:42
  • 62
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【Android应用开发技术:数据存储】数据库
举报原因:
原因补充:

(最多只允许输入30个字)