zoukankan      html  css  js  c++  java
  • 【Android Developers Training】 26. 在SQL数据库中保存数据

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。

    原文链接:http://developer.android.com/training/basics/data-storage/databases.html


    在数据库中保存数据,对于重复性的或者结构化的数据来说是很理想的,比如:联系人信息。这节课我们假定你对SQL数据库有一个大致的了解,并且帮助你在Android上开始使用SQLite数据库。你在Android的数据库上需要使用的APIs在android.database.sqlite包中。

     

    一). 定义一个架构(Schema)和契约(Contract)

    SQL数据库的主要核心之一是:一个数据库是如何组织的正式声明。架构所对应的就是你用来创建数据库的SQL语句声明。你会发现创建一个辅助类(companion class),即所谓的合同类(contract class),它通过一个系统的和自文档化的方式来显示地指定你的架构布局。

    一个合同类是一个容器,它包含了那些定义了URI,表和列的名称的常量。合同类允许你在同一个包内所有类之间使用相同的名字。这样可以使你想要改变列名时只需在一个地方修改,就能影响到所有使用到它的代码。

    一个良好的组织合同类的方式是:在类的根位置处,放置那些你的数据库的全局定义。然后为每一个表创建一个内部类,来枚举它的列。

    Note:

    通过实现基列(BaseColumns)接口,你的内部类可以继承一个主键字段,叫做:“_ID”。一些Android类,比如光标适配器(cursor adaptors)会期望它能够拥有这个。虽然这个不是必须的,但是它能够帮助你的数据库和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助手创建一个数据库

    一旦你定义了你的数据库,你应该实现一些方法来创建和维护你的数据库及表。下例是一些标准的创建和删除数据库的声明:

    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;

    就好像在设备内存(internal storage)中所存储的文件一样,Android会将你的数据库存储在一个和应用有关联的私有磁盘空间中。你的数据将是安全的,因为默认情况下这些数据不会被其他应用访问到。

    一个有用的APIs集合在SQLiteOpenHelper类中。当你使用这个类来获取你的数据库的引用时,系统会仅在需要时(不在应用启动时)执行一些可能需要消耗较长时间的操作,如:创建,更新数据库。所有你需要的是调用getWritableDatabase()或者getReadableDatabase()

    Note:

    因为它们可能会消耗比较长的时间,所以请确保你在一个后台线程调用getWritableDatabase()或者getReadableDatabase(),比如:AsyncTask或者IntentService

    要使用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());

    三). 将信息添加到数据库中

    可以将一个ContentValues对象传入insert()方法,来将数据添加到数据库中:

    // 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为空时,框架会插入一个NULL。(如果你将这个参数设置为“null”,那么当输入时空时,框架不会添加一个新列)

    四). 从数据库中读取数据

    为了从数据库中读取数据,使用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的一个get方法,比如:getString()或者getLong()。对于每个get方法,你必须传入你期望的列的索引号,你可以通过调用getColumnIndex()或者getColumnIndexOrThrow()来得到索引号。

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

    五). 从数据库中删除数据

    为了从数据库中删除一行数据,你需要提供一个选择标准来指定一行。数据库的API提供了一个创建选择标准的机制来防止SQL注入攻击。这个机制将选择语句分为了选择命令段(selection clause)和选择参数。命令段指定了要寻找的列,并且允许你可以结合一些列测试。参数是要测试的值,它是和命令段相对应的。因为这个结果和通常的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);
  • 相关阅读:
    动态规划 01背包问题
    日常水题 蓝桥杯基础练习VIP-字符串对比
    本博客导航
    2019 ICPC 南昌 (C E G L)
    [模板]线段树
    [模板]手写双端队列(或普通队列)
    2019 ICPC Asia Yinchuan Regional (G, H)
    与超级源点与超级汇点相关的两题POJ 1062, HDU 4725
    [模板]链式向前星
    [总结]关于反向建图
  • 原文地址:https://www.cnblogs.com/jdneo/p/3464754.html
Copyright © 2011-2022 走看看