Android 数据存储之 SQLit

SQLite

SQLite是一款开源的、轻量级的、嵌入式的、关系型数据库。它在2000年由D. Richard Hipp发布,可以支援Java、Net、PHP、Ruby、Python、Perl、C等几乎所有的现代编程语言,支持Windows、Linux、Unix、Mac OS、Android、IOS等几乎所有的主流操作系统平台。

SQLite的特性:
 
1. ACID事务
2. 零配置 – 无需安装和管理配置
3. 储存在单一磁盘文件中的一个完整的数据库
4. 数据库文件可以在不同字节顺序的机器间自由的共享
5. 支持数据库大小至2TB
6. 足够小, 大致3万行C代码, 250K
7. 比一些流行的数据库在大部分普通数据库操作要快
8. 简单, 轻松的API
9. 包含TCL绑定, 同时通过Wrapper支持其他语言的绑定
10. 良好注释的源代码, 并且有着90%以上的测试覆盖率
11. 独立: 没有额外依赖
12. Source完全的Open, 你可以用于任何用途, 包括出售它
13. 支持多种开发语言,C, PHP, Perl, Java, ASP.NET,Python

SQLite支持NULL、INTEGER、REAL、TEXT和BLOB数据类型,分别代表空值、整型值、浮点值、字符串文本、二进制对象。

SQLite提供了一组函数,详见:http://www.sqlite.org/lang_datefunc.html。
这样简单的数据类型设计更加符合嵌入式设备的要求。关于SQLite的更多资料,请参看:http://www.sqlite.org/


创建与删除数据库
 封装一个类去继承SQLiteOpenHelper  

  在构造函数中传入数据库名称与数据库版本号,数据库被创建的时候会调用onCreate(SQLiteDatabase db) 方法,

  数据库版本号发生改变的时候会调用onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)方法,可以方便的对软件游戏升级后做出相应处理避免覆盖安装数据库发生改变产生的错误。

  调用SQLiteOpenHelper的getReadableDatabase()方法去创建数据库,如果数据库不存在则创建 并且返回SQLiteDatabase对象,如果数据库存在则不创建只返回SQLiteDatabase对象。

  封装了一个DatabaseHelper类继承SQLiteOpenHelper

  添加与删除数据库中的表

  数据库是可以由多张数据表组成的 如果添加一张数据库的表的话 可以使用 数据库语句 create table 名称(内容) 来进行添加 。
  这里给出一条创建数据库的语句意思是创建一张表名称为gameInfo 表中包含的字段为_id为INTEGER 类型并且递增

public class DatabaseHelper extends SQLiteOpenHelper {
 
    private static final String DB_NAME = "mydata.db"; //数据库名称
    private static final int version = 1; //数据库版本
      
    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, version);
        // TODO Auto-generated constructor stub
    }
 
    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = "create table user(_id INTEGER PRIMARY KEY AUTOINCREMENT , username varchar(20) not null , password varchar(60) not null );";           
        db.execSQL(sql);
    }
 
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // TODO Auto-generated method stub
 
    }
 
}

SQLiteOpenHelper类介绍

  SQLiteOpenHelper是SQLiteDatabase的一个帮助类,用来管理数据库的创建和版本的更新。
  一般是建立一个类继承它,并实现它的onCreate和onUpgrade方法。

  方法名 方法描述
  SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version) 构造方法,一般是传递一个要创  建的数据库名称那么参数
  onCreate(SQLiteDatabase db) 创建数据库时调用
  onUpgrade(SQLiteDatabase db,int oldVersion , int newVersion) 版本更新时调用
  getReadableDatabase() 创建或打开一个只读数据库
  getWritableDatabase() 创建或打开一个读写数据库

下面来介绍调用的方法

创建数据库

这里特别的地方是通过调用了SQLiteOpenHelper类的getReadableDatabase()方法来实现创建一个数据库的,数据库的默认存放路径是/data-data-包名-databases/项目名.db

  DatabaseHelper database = new DatabaseHelper(this);//这段代码放到Activity类中才用this
  SQLiteDatabase db = null;
  db = database.getReadalbeDatabase(); 


如果同时希望存储一份数据库文件到SD卡上,需要自己创建一个数据库文件,如:/sdcard/包名/项目名.db,
注意要在AndroidManifest.xml中添加SD卡读写权限

<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>
 File path = new File("/sdcard/包名");// 创建目录
  File f = new File("/sdcard/包名/项目名.db");// 创建文件
  if (!path.exists()) {// 目录存在返回false
            path.mkdirs();// 创建一个目录
  }
  if (!f.exists()) {// 文件存在返回false
    try {
      f.createNewFile();//创建文件
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, null);
  //或者得到一个读写状态或只读状态的:SQLiteDatabase.openDatabase(fpath, null, writable ? SQLiteDatabase.OPEN_READWRITE : SQLiteDatabase.OPEN_READONLY)
  db = database.getWritableDatabase();

  然后就可以对SD卡上的数据库文件进行操作了

使用SQLiteOpenHelper创建管理SD卡中的数据库

使用Android中自带的SQLiteOpenHelper可以完成数据库的创建与管理,但有两点局限:
(1)数据库创建在内存卡中,大小受限,创建位置位于/data/data/应用程序名/databases中(可使用Eclispe的DDMS查看)。
(2)如果无法获取Root权限,则无法直接查看创建的数据库。

SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)
通过参数context,可以设置成sd卡上创建数据库,详细看下面代码。


 
SQLiteDatabase类为我们提供了很多种方法,而较常用的方法如下

  (返回值)方法名 方法描述
  (int) delete(String table,String whereClause,String[] whereArgs) 删除数据行的便捷方法
  (long) insert(String table,String nullColumnHack,ContentValues values) 添加数据行的便捷方法
  (int) update(String table, ContentValues values, String whereClause, String[] whereArgs) 更新数据行的便捷方法
  (void) execSQL(String sql) 执行一个SQL语句,可以是一个select或其他的sql语句
  (void) close() 关闭数据库
  (Cursor) query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having,   String orderBy, String limit) 查询指定的数据表返回一个带游标的数据集
  (Cursor) rawQuery(String sql, String[] selectionArgs) 运行一个预置的SQL语句,返回带游标的数据集(与上面的语句最大的区别就是防止SQL注入)

数据的添删改查分别可以通过2种途径来实现

数据的添加

1.使用insert方法
  

ContentValues cv = new ContentValues();//实例化一个ContentValues用来装载待插入的数据
  cv.put("username","Jack Johnson");//添加用户名
  cv.put("password","iLovePopMusic"); //添加密码
  db.insert("user",null,cv);//执行插入操作

2.使用execSQL方式来实现
 

 String sql = "insert into user(username,password) values ('Jack Johnson','iLovePopMuisc');//插入操作的SQL语句
  db.execSQL(sql);//执行SQL语句 

数据的删除

同样有2种方式可以实现
  

String whereClause = "username=?";//删除的条件
  String[] whereArgs = {"Jack Johnson"};//删除的条件参数
  db.delete("user",whereClause,whereArgs);//执行删除

使用execSQL方式的实现
 

String sql = "delete from user where username='Jack Johnson'";//删除操作的SQL语句
  db.execSQL(sql);//执行删除操作 

数据修改

同上,仍是2种方式
 

ContentValues cv = new ContentValues();//实例化ContentValues
  cv.put("password","iHatePopMusic");//添加要更改的字段及内容
  String whereClause = "username=?";//修改条件
  String[] whereArgs = {"Jack Johnson"};//修改条件的参数
  db.update("user",cv,whereClause,whereArgs);//执行修改 

使用execSQL方式的实现
 

 String sql = "update [user] set password = 'iHatePopMusic' where username='Jack Johnson'";//修改的SQL语句
  db.execSQL(sql);//执行修改 

数据查询

数据查询相对前面几种方法就复杂一些了,因为查询会带有很多条件

通过query实现查询的

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

各参数说明:

  table:表名称
  colums:列名称数组
  selection:条件子句,相当于where
  selectionArgs:条件语句的参数数组
  groupBy:分组
  having:分组条件
  orderBy:排序类
  limit:分页查询的限制
  Cursor:返回值,相当于结果集ResultSet

针对游标(Cursor)也提供了不少方法

  方法名称 方法描述
  getCount() 总记录条数
  isFirst() 判断是否第一条记录
  isLast() 判断是否最后一条记录
  moveToFirst() 移动到第一条记录
  moveToLast() 移动到最后一条记录
  move(int offset) 移动到指定的记录
  moveToNext() 移动到吓一条记录
  moveToPrevious() 移动到上一条记录
  getColumnIndex(String columnName) 获得指定列索引的int类型值

实现代码

Cursor c = db.query("user",null,null,null,null,null,null);//查询并获得游标
  if(c.moveToFirst()){//判断游标是否为空
    for(int i=0;i<c.getCount();i++){
        c.move(i);//移动到指定记录
        String username = c.getString(c.getColumnIndex("username");
        String password = c.getString(c.getColumnIndex("password"));
    }
  } 

通过rawQuery实现的带参数查询

  Cursor c = db.rawQuery("select * from user where username=?",new Stirng[]{"Jack Johnson"});
  if(cursor.moveToFirst()) {
    String password = c.getString(c.getColumnIndex("password"));
  } 

/**这里有几个有用的SQL语句*********************************************/

查询数据库有哪些表
select name from sqlite_master where type = 'table';  

查询表字段信息
pragma table_info('表名')

/***********************************************/

/**
里面有几个问题:如何管理Cursor的生命周期,如果包装Cursor,Cursor结果集都需要注意什么。
如果手动去管理Cursor的话会非常的麻烦,还有一定的风险,处理不当的话运行期间就会出现异常,幸好Activity为我们提供了startManagingCursor(Cursor cursor)方法,
它会根据Activity的生命周期去管理当前的Cursor对象。 startManagingCursor方法会根据Activity的生命周期去管理当前的Cursor对象的生命周期,就是说当Activity停止时他会自动调用Cursor的deactivate方法,禁用游标,
当Activity重新回到屏幕时它会调用Cursor的requery方法再次查询,当Activity摧毁时,被管理的Cursor都会自动关闭释放。 如何包装Cursor:我们会使用到CursorWrapper对象去包装我们的Cursor对象,实现我们需要的数据转换工作,这个CursorWrapper实际上是实现了Cursor接口。
我们查询获取到的Cursor其实是Cursor的引用,而系统实际返回给我们的必然是Cursor接口的一个实现类的对象实例,我们用CursorWrapper包装这个实例,
然后再使用SimpleCursorAdapter将结果显示到列表上。 Cursor结果集需要注意些什么:一个最需要注意的是,在我们的结果集中必须要包含一个“_id”的列,否则SimpleCursorAdapter就会翻脸不认人,为什么一定要这样呢?
因为这源于SQLite的规范,主键以“_id”为标准。 解决办法有三: 第一,建表时根据规范去做; 第二,查询时用别名,例如:SELECT id AS _id FROM person; 第三,在CursorWrapper里做文 *
*/ Cursor c = mgr.queryTheCursor(); startManagingCursor(c); //托付给activity根据自己的生命周期去管理Cursor的生命周期 CursorWrapper cursorWrapper = new CursorWrapper(c) { @Override public String getString(int columnIndex) { if (columnName.equals("_id")) { //如果主键命名不是 _id return super.getColumnIndex("id"); } return super.getColumnIndexOrThrow(columnName); }; //确保查询结果中有"_id"列 SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, cursorWrapper,
                    new String[]{"name", "info"}, new int[]{android.R.id.text1, android.R.id.text2}); ListView listView = (ListView) findViewById(R.id.listView); listView.setAdapter(adapter);

注意:使用完数据库后需关闭close()

//////////////////////////////
通过java映射的方式操作SQLite
//////////////////////////////

DBContextWrapper.class 继承ContextWrapper,实现SQLiteOpenHelper在内存和SD创建打开数据库
DBHelper.class 继承SQLiteOpenHelper类,实现数据库SQLite的基本操作
DatabaseColumn.class 实现BaseColumns接口,实现通过类创建表
DetailColumn.class 继承DatabaseColumn类,具体创建表的属性

****************************************************************************************

public class DBContextWrapper extends ContextWrapper {

    public DBContextWrapper(Context base) {
        super(base);
    }
    
    private boolean isSDcardExist() {
        return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
    }

    @Override
    public File getDatabasePath(String name) {
        if (isSDcardExist()) {
            String path = Environment.getExternalStorageDirectory() + "/Android/data/" + getPackageName();
            if (!(new File(path)).exists()) {
                (new File(path)).mkdirs();
            }
            File dbFile = new File(path + "/" + name);
            if (!dbFile.exists()) {
                try {
                    dbFile.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                    dbFile = null;
                }
            }
            return dbFile;
        }
        return super.getDatabasePath(name);
    }
    
    @Override
    public boolean deleteDatabase(String name) {
        return super.deleteDatabase(name);
    }
    
    /**
     * 
     * 2.3及以下会调用 
     * @see android.content.ContextWrapper#openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory)
     */
    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode,
            CursorFactory factory) {
        if(isSDcardExist())
            return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), factory);
        return super.openOrCreateDatabase(name, mode, factory);
    }
    
    /**
     * 
     * TODO 
     * @see android.content.ContextWrapper#openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler)
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public SQLiteDatabase openOrCreateDatabase(String name, int mode,
            CursorFactory factory, DatabaseErrorHandler errorHandler) {
        if(isSDcardExist())
            return SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), factory);
        return super.openOrCreateDatabase(name, mode, factory, errorHandler);
    }
}
public class DBHelper extends SQLiteOpenHelper {

    private static final String DB_NAME = "eoecn";
    private static final int DB_VERSION = 2;

    private SQLiteDatabase db;
    
    private static DBHelper mdbHelper;
    
    public static DBHelper getInstance(Context context)
    {
        if(mdbHelper==null)
        {
            mdbHelper=new DBHelper(new DBContextWrapper(context));
        }
        return mdbHelper;
    }

    private DBHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    private DBHelper(Context context, String name, CursorFactory factory,
            int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        this.db = db;
        operateTable(db, "");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // TODO Auto-generated method stub
        if (oldVersion == newVersion) {
            return;
        }
        operateTable(db, "DROP TABLE IF EXISTS ");
        onCreate(db);
    }

    public void operateTable(SQLiteDatabase db, String actionString) {
        Class<DatabaseColumn>[] columnsClasses = DatabaseColumn.getSubClasses();
        DatabaseColumn columns = null;

        for (int i = 0; i < columnsClasses.length; i++) {
            try {
                columns = columnsClasses[i].newInstance();
                if ("".equals(actionString) || actionString == null) {
                    db.execSQL(columns.getTableCreateor());
                } else {
                    db.execSQL(actionString + columns.getTableName());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    public long insert(String Table_Name, ContentValues values) {
        if (db == null)
            db = getWritableDatabase();
        return db.insert(Table_Name, null, values);
    }

    /**
     *
     * @param Table_Name
     * @param id
     * @return 影响行数
     */
    public int delete(String Table_Name, int id) {
        if (db == null)
            db = getWritableDatabase();
        return db.delete(Table_Name, BaseColumns._ID + "=?",
                new String[] { String.valueOf(id) });
    }

    /**
     * @param Table_Name
     * @param values
     * @param WhereClause
     * @param whereArgs
     * @return 影响行数
     */
    public int update(String Table_Name, ContentValues values,
            String WhereClause, String[] whereArgs) {
        if (db == null) {
            db = getWritableDatabase();
        }
        return db.update(Table_Name, values, WhereClause, whereArgs);
    }

    public Cursor query(String Table_Name, String[] columns, String whereStr,
            String[] whereArgs) {
        if (db == null) {
            db = getReadableDatabase();
        }
        return db.query(Table_Name, columns, whereStr, whereArgs, null, null,
                null);
    }

    public void closeDb() {
        if (db != null) {
            db.close();
            db = null;
        }
    }

}

****************************************************************************************

public abstract class DatabaseColumn implements BaseColumns {
    /**
     * The identifier to indicate a specific ContentProvider
     */
    public static final String AUTHORITY = "cn.eoe.app.provider";
    /**
     * The database's name
     */
    public static final String DATABASE_NAME = "eoecn.db";
    /**
     * The version of current database
     */
    public static final int DATABASE_VERSION = 1;
    /**
     * Classes's name extends from this class.
     */
    public static final String[] SUBCLASSES = new String[] {
            "cn.eoe.app.db.BlogColumn", "cn.eoe.app.db.NewsColumn",
            "cn.eoe.app.db.DetailColumn", "cn.eoe.app.db.ImageCacheColumn",
            "cn.eoe.app.db.RequestCacheColumn" };

    public String getTableCreateor() {
        return getTableCreator(getTableName(), getTableMap());
    }

    /**
     * Get sub-classes of this class.
     *
     * @return Array of sub-classes.
     */
    @SuppressWarnings("unchecked")
    public static final Class<DatabaseColumn>[] getSubClasses() {
        ArrayList<Class<DatabaseColumn>> classes = new ArrayList<Class<DatabaseColumn>>();
        Class<DatabaseColumn> subClass = null;
        for (int i = 0; i < SUBCLASSES.length; i++) {
            try {
                subClass = (Class<DatabaseColumn>) Class.forName(SUBCLASSES[i]);
                classes.add(subClass);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                continue;
            }
        }
        return classes.toArray(new Class[0]);
    }

    /**
     * Create a sentence to create a table by using a hash-map.
     *
     * @param tableName
     *            The table's name to create.
     * @param map
     *            A map to store table columns info.
     * @return
     */
    private static final String getTableCreator(String tableName,
            Map<String, String> map) {
        String[] keys = map.keySet().toArray(new String[0]);
        String value = null;
        StringBuilder creator = new StringBuilder();
        creator.append("CREATE TABLE ").append(tableName).append("( ");
        int length = keys.length;
        for (int i = 0; i < length; i++) {
            value = map.get(keys[i]);
            creator.append(keys[i]).append(" ");
            creator.append(value);
            if (i < length - 1) {
                creator.append(",");
            }
        }
        creator.append(")");
        return creator.toString();
    }

    abstract public String getTableName();

    abstract public Uri getTableContent();

    abstract protected Map<String, String> getTableMap();

}

****************************************************************************************

public class DetailColumn extends DatabaseColumn {

    public static final String TABLE_NAME = "detailRecord";
    // public static final String KEY_WORD = "key_word";
    // public static final String CONTENT_ID = "contentID";
    public static final String URL = "url";
    public static final String KEY = "key";
    public static final String GOOD = "good";
    public static final String BAD = "bad";
    public static final String COLLECT = "collect";

    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
            + "/" + TABLE_NAME);
    private static final Map<String, String> mColumnMap = new HashMap<String, String>();
    static {
        mColumnMap.put(_ID, "integer primary key autoincrement");
        mColumnMap.put(URL, "text");
        mColumnMap.put(KEY, "text");
        mColumnMap.put(GOOD, "integer");
        mColumnMap.put(BAD, "integer");
        mColumnMap.put(COLLECT, "integer");
    }

    @Override
    public String getTableName() {
        // TODO Auto-generated method stub
        return TABLE_NAME;
    }

    @Override
    public Uri getTableContent() {
        // TODO Auto-generated method stub
        return CONTENT_URI;
    }

    @Override
    protected Map<String, String> getTableMap() {
        // TODO Auto-generated method stub
        return mColumnMap;
    }

}
原文地址:https://www.cnblogs.com/rfheh/p/4164773.html