Android高级编程(笔记)-第6章 数据存储、检索和共享—3--SQLite数据库

    SQLite为Android提供了关系数据库功能

    所有的数据库存储在设备的/data/data/<package_name>/databases文件夹下

 

一、SQLite简介

    SQLite是关系数据库管理系统(RDBMS),特点是:

    开源

    兼容标准

    轻量级

    Single-tier

 

    实现为一简洁的C库,包含在Android软件栈中

    作为应用程序的一部分而提供功能,这样可减少应用程序的外部依赖、最小化延迟并简化事务锁和同步。

    与其它数据引擎区别的是,从每一行的第一列中分配或提取值时,不需要进行严格的类型检查

二、Cursor和内容值

    用ContentValues 对象向数据库中插入一行,它代表一行,列名用于对它值的映射

    Cursor对象

    Cursor对象中的功能来操作导航结果:

    ● moveToFirst

    ●moveToNext

    ●moveToPrevious

    ●getCount

    ●getColumnName

    ●getColumnNames

    ●getColumnIndexOrThrow

    ●moveToPosition

    ●getPosition

    startManagerCursor方法管理活动中的Cursor资源

三、使用Android数据库

    创建一个helper类来简化对数据库的交互

    Android中的一个标准数据库adapter类的框架:包含对SQLiteOpenHelper类的扩展

 

   1: import android.content.ContentValues;
   2: import android.content.Context;
   3: import android.database.*;
   4: import android.database.sqlite.*;
   5: import android.database.sqlite.SQLiteDatabase.CursorFactory;
   6: import android.util.Log;
   7:  
   8: public class MyDBAdapter {
   9:   private static final String DATABASE_NAME = "myDatabase.db";
  10:   private static final String DATABASE_TABLE = "mainTable";
  11:   private static final int DATABASE_VERSION = 1;
  12:  
  13:   // where子句中使用的索引(键)列的名字
  14:   public static final String KEY_ID = "_id";
  15:  
  16:   // 数据库中的第一列的名称和索引
  17:   public static final String KEY_NAME = "name"; 
  18:   public static final int NAME_COLUMN = 1;
  19:   // 待实现,为你的表中的第一列创建公共域
  20:   
  21:   // 创建一个数据库的sql语句
  22:   private static final String DATABASE_CREATE = "create table " + 
  23:     DATABASE_TABLE + " (" + KEY_ID + 
  24:     " integer primary key autoincrement, " +
  25:     KEY_NAME + " text not null);";
  26:  
  27:   // 保存数据库实例的变量
  28:   private SQLiteDatabase db;
  29:   //使用数据库的应用程序的上下文
  30:   private final Context context;
  31:   // 数据库打开\更新helper类
  32:   private myDbHelper dbHelper;
  33:  
  34:   public MyDBAdapter(Context _context) {
  35:     context = _context;
  36:     dbHelper = new myDbHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
  37:   }
  38: public MyDBAdapter open() throws SQLException {
  39:     db = dbHelper.getWritableDatabase();
  40:     return this;
  41:   }
  42:  
  43:   public void close() {
  44:     db.close();
  45:   }
  46:  
  47:   public long insertEntry(MyObject _myObject) {
  48:     ContentValues contentValues = new ContentValues();
  49:     // 待实现:填充ContentValues来表示新的行
  50:    return db.insert(DATABASE_TABLE, null, contentValues);
  51:   }
  52:  
  53:   public boolean removeEntry(long _rowIndex) {
  54:     return db.delete(DATABASE_TABLE, KEY_ID + "=" + _rowIndex, null) > 0;
  55:   }
  56:  
  57:   public Cursor getAllEntries () {
  58:     return db.query(DATABASE_TABLE, new String[] {KEY_ID, KEY_NAME}, 
  59:                     null, null, null, null, null);
  60:   }
  61:  
  62:   public MyObject getEntry(long _rowIndex) {
  63:     MyObject objectInstance = new MyObject();
  64:     // 待实现,从数据库中返回一个行的Cursor,并使用该行的
  65:     // 值填充一个MyObject实例
  66:     return objectInstance;
  67:   }
  68:  
  69:   public int updateEntry(long _rowIndex, MyObject _myObject) {
  70:     String where = KEY_ID + " = " + _rowIndex;
  71:     ContentValues contentValues = new ContentValues();
  72:     // 待实现,根据新的对象填充ContentValues
  73:     return db.update(DATABASE_TABLE, contentValues, where, null);
  74:   }
  75:  
  76:   private static class myDbHelper extends SQLiteOpenHelper {
  77:  
  78:     public myDbHelper(Context context, String name, CursorFactory factory, int version) {
  79:       super(context, name, factory, version);
  80:     }
  81:  
  82:     // 当磁盘上没数据库时候调用,helper需要创建
  83:     // 一个新的数据库 
  84:     @Override
  85:     public void onCreate(SQLiteDatabase _db) {
  86:       _db.execSQL(DATABASE_CREATE);
  87:     }
  88:  
  89:     // 当存储数据库不匹配时调用,也就是说,
  90:     // 磁盘上的数据库版本需要更新到当前版本
  91:     @Override
  92:     public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) {
  93:       // 日志中记录数据库版本更新
  94:       Log.w("TaskDBAdapter", "Upgrading from version " + 
  95:                              _oldVersion + " to " +
  96:                              _newVersion + ", which will destroy all old data");
  97:         
  98:       // 更新现有数据库,使其为最新版本
  99:       // 通过比较_oldVersion和_newVersion 值来处理多个以前的版本
 100:       // 最简单的情况是删除旧的表,创建一个新表
 101:  
 102:       // 
 103:       _db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE);
 104:       // 创建一个新表
 105:       onCreate(_db);
 106:     }
 107:   }
 
1、使用SQLiteOpenHelper
SQLiteOpenHelper抽象类封装了创建、打开和修改数据库的最佳实践模式。
上述代码通过重写构造函数、onCreate和onUpgradeg来扩展SQLiteOpenHelper类通过传递上下文、数据库名称、当前版本以及一个CursorFactory创建helper类。
调用getWritableDatabase或getReadableDatabase打开一个可写/可读的数据库实例(最好提供一个getReadableDatabase方法的回退)
   1: dbHelper = new myDbHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
   2:  
   3: SQLiteDatabase db;
   4:     try {
   5:         db = dbHelper.getWritableDatabase();
   6:     } catch (SQLiteException ex) {
   7:         db = dbHelper.getWritableDatabase();
   8:     }

针对数据库不存在时helper调用onCreate处理;版本改变时onUpgrade会触发.对于这两种情况,get<Write/read>Database返回已经存在、新创建的和更新过的数据库。

    2、不使用SQLiteOpenHelper打开和创建数据库

    调用openOrCreateDatabase

myDatabase = openOrCreateDatabase(DATABASE_NAME,Context.MODE_PRIVATE,null);
myDatabase.execSQL(DATABASE_CREATE);

    3.Android数据库设计注意事项

    (1)表中不存储文件,只存储使用完全限定的内容提供器URI来表示的路径

    (2)表中应该包含一个自动增加的键域,为第一行提供唯一索引

    4、查询数据库

    查询返回一个结果集的Cursor

    query方法原型(还有其它方法签名):

public Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) 

参数:

boolean distinct:是否包含唯一的值

String table:查询表的名称

String[] columns:列出包含在结果中列的字符串数组

String selection:“where”子句,要返回的行。包含“?”将被选择参数中的值替换

String[] selectionArgs:选择参数字符串数组,替换“where”子句中的“?”

String groupBy:定义返回行的分组方式

String having:过滤器,如果包含groupBy,则定义包含哪些行的分组

String orderBy:行的顺序

String limit:定义对返回行的限制

CancellationSignal cancellationSignal

 

    返回一个特定表中部分或全部的行的代码框架:

//返回第一列和第三列的所有行,没重复
String[] result_columns = new String{}[KEY_ID,KEY_COL1,KEY_COL3];
Cursor allRows = myDatabase.query(true,DATABASE_TABLE,result_columns,null,null,null,null,null,null);

//返回第三列中等于一个特定值的所有列,并按第5列排序
String where = KEY_COL3 + "=" + requiredValue;
String order = KEY_COL5;
Cursor myResult = myDatabase.query(DATABASE_TABLE,null,where,null,null,null,order);

5.从Cursor中提取结果

首先在返回的结果集Cursor中定位行moveTo<location>

    get方法

String columnValue = myResult.getString(columnIndex);

6 、添加、更新和删除行

  SQLiteDatabase类提供insert delete update等基本数据库操作方法,execSQL方法提供操作数据的有效方法

视图的Cursor的refreshQuery方法将对底层数据的修改进行提交。

(1)插入一个新行

构建ContentValues对象,put方法为列提供值,用instert插入数据库

 

 

   1: //创建要插入一个新行
   2: ContentVlaues newValues = new ContentValues();
   3: //为每行赋值
   4: newValues.put(COLUMN_NAME,newVlaue);
   5: [...Repeat for each colum...]
   6: //把行插入到表中
   7: myDatabase.insert(DATABASE_TABLE,null,newValues);

(2)更新数据库中的行

//定义要更新的新行
ContentVlaues updatedValues = new ContentValues();
//为每行赋值
updatedValues.put(COLUMN_NAME,newVlaue);
[...Repeat for each colum...]

String where = KEY_ID + "=" + rowID;

//使用新的值更新指定索引的行
myDatabase.update(DATABASE_TABLE,updatedValues,where,null);
//把行插入到表中
myDatabase.insert(DATABASE_TABLE,null,newValues);

(3)删除行

myDatabase.delete(DATABASE_TABLE,KEY_ID + "=" +rowId,null);
 
7.保存To-Do-List

     将前面的To-Do List改进,创建一个私有的数据库来保存待办事项.

      (1)首先创建一个新的ToDoDBAdapter类。

 

 

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.util.Log;

/** Database Adapter for persisting Todo Items */
public class ToDoDBAdapter {
private static final String DATABASE_NAME = "todoList.db";
private static final String DATABASE_TABLE = "todoItems";
private static final int DATABASE_VERSION = 1;

private SQLiteDatabase db;
private final Context context;

public ToDoDBAdapter(Context _context) {
context = _context;
}

(2)定义列的名称和索引,从查询返回的Cursor中查询到正确的列

 

public static final String KEY_ID = "_id";
public static final String KEY_TASK = "task";
public static final String KEY_CREATION_DATE = "creation_date";

public static final int TASK_COLUMN = 1;
public static final int CREATION_DATE_COLUMN = 2;

(3)创建taskDBOpenHelper,它扩展了SQLiteOpenHelper.类中,重写onCreate和onUpgrade处理数据库的创建和更新逻辑

   1: private static class toDoDBOpenHelper extends SQLiteOpenHelper {
   2:  
   3:       public toDoDBOpenHelper(Context context, String name, CursorFactory factory, int version) {
   4:         super(context, name, factory, version);
   5:       }
   6:  
   7:       /** 创建一个新数据库的SQL语句*/
   8:       private static final String DATABASE_CREATE = "create table " + 
   9:         DATABASE_TABLE + " (" + KEY_ID + " integer primary key autoincrement, " +
  10:         KEY_TASK + " text not null, " + KEY_CREATION_DATE + " integer);";
  11:  
  12:       @Override
  13:       public void onCreate(SQLiteDatabase _db) {
  14:         _db.execSQL(DATABASE_CREATE);
  15:       }
  16:  
  17:       @Override
  18:       public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) {
  19:         Log.w("TaskDBAdapter", "Upgrading from version " + 
  20:                                _oldVersion + " to " +
  21:                                _newVersion + ", which will destroy all old data");
  22:  
  23:         // Drop the old table.
  24:         _db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE);
  25:         // Create a new one.
  26:         onCreate(_db);
  27:       }
  28:     }

(4)创建toDoDBOpenHelper类的一个实例,构造函数中为它赋值

   1: private toDoDBOpenHelper dbHelper;
   2:  
   3:   public ToDoDBAdapter(Context _context) {
   4:     context = _context;
   5:     dbHelper = new toDoDBOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
   6:   }

(5)close方法

/** Close the database */
public void close() {
db.close();
}

 

(6)open方法用到toDoDBOpenHelper类,getWritableDatabase创建数据库和版本检查

/** Open the database */
public void open() throws SQLiteException {
try {
db = dbHelper.getWritableDatabase();
}
catch (SQLiteException ex) {
db = dbHelper.getReadableDatabase();
}
}

(7)添加、删除和修改条目

 

   1: /** 插入一个新的任务*/
   2:  public long insertTask(ToDoItem _task) {
   3:    // 创建要插入的一行新值
   4:    ContentValues newTaskValues = new ContentValues();
   5:  
   6:    // 为每一行赋值
   7:    newTaskValues.put(KEY_TASK, _task.getTask());
   8:    newTaskValues.put(KEY_CREATION_DATE, _task.getCreated().getTime());
   9:    
  10:    // 插入行
  11:    return db.insert(DATABASE_TABLE, null, newTaskValues);
  12:  }
  13:  
  14:  /** 根据索引删除一个任务*/
  15:  public boolean removeTask(long _rowIndex) {
  16:    return db.delete(DATABASE_TABLE, KEY_ID + "=" + _rowIndex, null) > 0;
  17:  }
  18:  
  19:  /** 更新一个任务 */
  20:  public boolean updateTask(long _rowIndex, String _task) {
  21:    ContentValues newValue = new ContentValues();
  22:    newValue.put(KEY_TASK, _task);
  23:    return db.update(DATABASE_TABLE, newValue, KEY_ID + "=" + _rowIndex, null) > 0;
  24:  }

 

(8)用helper方法查询:3个方法:

a、返回所有条目;b、作为一个Cursor返回特定行;c、返回一个强类型ToDoItem

 /** Return a Cursor to all the Todo items */
  public Cursor getAllToDoItemsCursor() {
      return db.query(DATABASE_TABLE, new String[] { KEY_ID, KEY_TASK, KEY_CREATION_DATE}, null, null, null, null, null);
    }

  /** Return a Cursor to a specific row */
    public Cursor setCursorToToDoItem(long _rowIndex) throws SQLException {
      Cursor result = db.query(true, DATABASE_TABLE, new String[] {KEY_ID, KEY_TASK},
                               KEY_ID + "=" + _rowIndex, null, null, null, null, null);
      
      if ((result.getCount() == 0) || !result.moveToFirst()) {
        throw new SQLException("No Todo items found for row: " + _rowIndex);
      }
      return result;
    }

    /** Return a Todo Item based on its row index */
    public ToDoItem getToDoItem(long _rowIndex) throws SQLException {
      Cursor cursor = db.query(true, DATABASE_TABLE, new String[] {KEY_ID, KEY_TASK},
                               KEY_ID + "=" + _rowIndex, null, null, null, null, null);
      if ((cursor.getCount() == 0) || !cursor.moveToFirst()) {
        throw new SQLException("No to do item found for row: " + _rowIndex);
      }

      String task = cursor.getString(TASK_COLUMN);
      long created = cursor.getLong(CREATION_DATE_COLUMN);
          
      ToDoItem result = new ToDoItem(task, new Date(created));
      return result;  
    }
posted @ 2012-07-19 15:42  庙子  阅读(422)  评论(0编辑  收藏  举报