zoukankan      html  css  js  c++  java
  • Android高级编程(笔记)第6章 数据存储、检索和共享—3SQLite数据库

        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;  
        }
  • 相关阅读:
    如何提高送测版本的质量(开发篇)?
    web安全测试资料
    如何带领新人
    华为新人180天详细培养计划
    畠山芳雄:新员工培训的十三条黄金法则
    BUG级别定义标准
    EF-Linq将查询结果转换为List<string>
    在差距中进步,在记录中进步,
    Error: [ng:areq] Argument 'xxxx' is not a function, got undefined
    Book-编程珠玑-第一章
  • 原文地址:https://www.cnblogs.com/mcsm/p/2599413.html
Copyright © 2011-2022 走看看