Android存储-SQLite数据库存储数据(三)
前言
SQLite数据库一款轻量级关系数据库,轻量型、跨平台、多语言接口、安全、支持事务处理,而且运算速度快,占用资源少。Android数据持久化存储的内部存储主要存在在内部存储设备上,恰恰Android内部设备存储空间往往是有限的,如果不有效的管理内部存储空间,很有可能导致手机直接崩溃无法使用。而且SQLite轻量级及相关有点正好符合了Android本身的特性。其实Android另外两个存储方式:文件存储和SharePreferences存储中的SharePreferences存储就属于轻量型存储机智。而对于文件存储要去也是尽量存储到外部存储设备中。因此SQLite本身的优点特别适合在移动设备上使用。
本博客主要围绕SQLite展开讲述,其实已经非常便捷和成熟的SQLite封装的工具,有兴趣的可以可以网上搜索一下几个框架去了解:
开源库 ---OrmLite开源库 ---greenDAO开源库 ---SugarORM开源库 ---ActiveAndroid开源库 ---LitePal开源库 ---realm
重述SQLite数据优点
在这里重复简述一下SQLite的特点:
1、SQLite作为一个轻型的关系型数据库;2、运算速度非常快,占用资源很少;3、SQLite不仅支持标准的SQL语法,还遵守了数据库的 ACID 事务,上手快;4、提供了零配置运行模式;5、无需服务进程,轻量、跨平台、多语言接口及安全性等;
SQLite数据库的使用
一、创建数据库方式
创建数据主要有四种方式:
1、create 在内存中创建数据库,当数据库关闭时即销毁;
2、openOrCreateDatabase 创建并打开数据库;
3、继承SQLiteOpenHelper类,且通过getWritableDatabase和getReadableDatabase方法创建;
4、openDatabase 创建并打开数据库,可以指定打开方式;
create简述
/** * Create a memory backed SQLite database. Its contents will be destroyed * when the database is closed. * * Sets the locale of the database to the the system's current locale. * Call {@link #setLocale} if you would like something else.
* * @param factory an optional factory class that is called to instantiate a * cursor when query is called * @return a SQLiteDatabase object, or null if the database can't be created */ public static SQLiteDatabase create(CursorFactory factory) { // This is a magic string with special meaning for SQLite. return openDatabase(SQLiteDatabaseConfiguration.MEMORY_DB_PATH, factory, CREATE_IF_NECESSARY); }
通过查看源码,我们可以发现,使用create方式创建数据库,最终是调用openDatabase方式来实现数据库的创建。
openOrCreateDatabase简述
/** * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY). */ public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) { return openOrCreateDatabase(file.getPath(), factory); } /** * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY). */ public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) { return openDatabase(path, factory, CREATE_IF_NECESSARY, null); } /** * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler). */ public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory, DatabaseErrorHandler errorHandler) { return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler); }
通过查看源码,openOrCreateDatabase方式创建数据库,不管是带两个参数,还是三个,最终是通过调用openDatabase方式来实现数据库的创建。
getWritableDatabase和getReadableDatabase简述
* Create and/or open a database that will be used for reading and writing. * The first time this is called, the database will be opened and * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be * called. * * <p>Once opened successfully, the database is cached, so you can * call this method every time you need to write to the database. * (Make sure to call {@link #close} when you no longer need the database.) * Errors such as bad permissions or a full disk may cause this method * to fail, but future attempts may succeed if the problem is fixed.</p> * * <p class="caution">Database upgrade may take a long time, you * should not call this method from the application main thread, including * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}. * * @throws SQLiteException if the database cannot be opened for writing * @return a read/write database object valid until {@link #close} is called */ public SQLiteDatabase getWritableDatabase() { synchronized (this) { return getDatabaseLocked(true); } }private SQLiteDatabase getDatabaseLocked(boolean writable) { ... SQLiteDatabase db = mDatabase; try { mIsInitializing = true; if (db != null) { if (writable && db.isReadOnly()) { db.reopenReadWrite(); } } else if (mName == null) { db = SQLiteDatabase.create(null); } else { try { if (DEBUG_STRICT_READONLY && !writable) { final String path = mContext.getDatabasePath(mName).getPath(); //位置1 db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler); } else { //位置2 db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ? Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0, mFactory, mErrorHandler); } } catch (SQLiteException ex) { if (writable) { throw ex; } Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", ex); final String path = mContext.getDatabasePath(mName).getPath(); //位置3 db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler); } } ... }
同样通过分析源码,我们可以看到getWritableDatabase和getReadableDatabase两个方法最终会调用getDatabaseLocked方法,而在getDatabaseLocked方法内,通过位置1、位置2和位置3我们可以分析得出结果,getWritableDatabase和getReadableDatabase方式创建数据,最终也是通过调用openDatabase方式来实现数据库的创建。
openDatabase简述
通过分析create方式、方式、openOrCreateDatabase、getWritableDatabase和getReadableDatabase方式创建数据的源码,得出在最终是通过openDatabase方式来创建数据库的,所以这里我们做种分析一下openDatabase方法;
/** * Open the database according to the flags {@link #OPEN_READWRITE} * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}. * * Sets the locale of the database to the the system's current locale. * Call {@link #setLocale} if you would like something else.
* * Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be * used to handle corruption when sqlite reports database corruption.
* * @param path to database file to open and/or create * @param factory an optional factory class that is called to instantiate a * cursor when query is called, or null for default * @param flags to control database access mode * @param errorHandler the {@link DatabaseErrorHandler} obj to be used to handle corruption * when sqlite reports database corruption * @return the newly opened database * @throws SQLiteException if the database cannot be opened */ public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags, DatabaseErrorHandler errorHandler) { SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler); db.open(); return db; }
openDatabase方法分别有四个参数:
第一个参数:path代表着数据库的路径(如果是在默认路径/data/data/
第二个参数:factory代表着在创建Cursor对象时,使用的工厂类,如果为null的话,则使用默认的工厂(这里我们可以实现自己的工厂进行某些数据处理);
第三个参数:flags代表的是创建表时的一些权限设置,多个权限之间用|分隔:
权限 | 权限说明 |
---|---|
OPEN_READONLY | 代表的是以只读方式打开数据库(常量值为:1) |
OPEN_READWRITE | 代表以读写方式打开数据库(常量值为:0) |
CREATE_IF_NECESSARY | 当数据库不存在时创建数据库 |
NO_LOCALIZED_COLLATORS | 打开数据库时,不根据本地化语言对数据库进行排序(常量值为:16) |
第四个参数:errorHandler数据损坏时的回调参数(可null);
二、创建数据库
以上介绍的四种创建数据库的方式,最终目的就是为了得到SQLiteDatabase对象,因为只有在得到了SQLiteDatabase
对象,才能对相应的数据库增、删、改、查
,一下用代码形式简单表述一下用以上四种方式获取SQLiteDatabase对象。
create方式获取数据库对象
//在内存中创建数据库,当数据库关闭时即销毁;SQLiteDatabase database = null; database = SQLiteDatabase.create(null);
openOrCreateDatabase方式获取数据库对象
SQLiteDatabase database = null;method 1://file_path为文件存储路径File file = new File("file_path");database = SQLiteDatabase.openOrCreateDatabase(file, null);method 2://data_name为数据名称,且创建的数据库是在默认路径/data/data//databases/下 String dataName = "data_name.db";database = SQLiteDatabase.openOrCreateDatabase(dataName, null);//第3种方式和第2中方式没有本质上的区别,区别在于是否对数据损坏进行监测method 3://data_name为数据名称,且创建的数据库是在默认路径/data/data//databases/下 String dataName = "data_name.db";database = SQLiteDatabase.openOrCreateDatabase(dataName, null, null);
openDatabase方式获取数据库对象
SQLiteDatabase database = null;method 1://data_name为数据名称,且创建的数据库是在默认路径/data/data//databases/下 String dataName = "data_name.db";database = SQLiteDatabase.openDatabase(dataName, null, CREATE_IF_NECESSARY);method 2://data_name为数据名称,且创建的数据库是在默认路径/data/data//databases/下 String dataName = "data_name.db";database = SQLiteDatabase.openDatabase(dataName, null, CREATE_IF_NECESSARY, null);
getWritableDatabase和getReadableDatabase方式获取数据库对象
在使用前简单介绍一下SQLiteOpenHelper抽象类,SQLiteOpenHelper是SQLiteDatabase的帮助类, 用于管理数据库的创建和升级。
需要一个继承SQLiteOpenHelper抽象类的自定义类,我们通过继承该类,然后重写数据库创建以及更新的方法, 我们还可以通过该类的对象获得数据库实例,或者关闭数据库;
a、首先需要实现一个自定义类继承于SQLiteOpenHelper
public class DBOpenHelper extends SQLiteOpenHelper { // 相当于 SQLiteDatabase openDatabase(String, CursorFactory) public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } // 相当于 SQLiteDatabase openDatabase(String, CursorFactory, DatabaseErrorHandler) @TargetApi(Build.VERSION_CODES.HONEYCOMB) public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler) { super(context, name, factory, version, errorHandler); } // 相当于 SQLiteDatabase openDatabase(String , OpenParams); @TargetApi(Build.VERSION_CODES.P) public DBOpenHelper(Context context, String name, int version, SQLiteDatabase.OpenParams openParams) { super(context, name, version, openParams); } // 创建数据文件时调用,此时适合创建新表 @Override public void onCreate(SQLiteDatabase db) { } // 更新数据库版本时调用,适合更新表结构或创建新表 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }}
b、其次通过实现DBOpenHelper类,在通过实现该类得到的对象,调用getWritableDatabase或getReadableDatabase方法实现创建数据库,如下:
SQLiteDatabase database = null;//data_name为数据名称,且创建的数据库是在默认路径/data/data//databases/下 String dataName = "data_name.db";// 生成 helper 对象,可以打开数据库文件。文件名可以是相对路径或绝对路径DBOpenHelper dbHelper = new DBOpenHelper(this, dataName, null, 1);// 用读写的方式打开数据库文件database = dbHelper.getWritableDatabase();
备注:如果不采用第三方框架的情况下,只是在内部存储设备中创建数据库,最好在使用getWritableDatabase和getReadableDatabase方式获取数据库对象,如果是有需要使用外部数据,那么可以使用openOrCreateDatabase方式。
三、数据库的增删改查操作
在第二部分中,我们通过四种方式中的任何一种方式,获取SQLiteDatabase对象,通过该对象我们可以实现接下来的增删改查功能。
新增数据
/** * 向数据库插入一行数据 * * @param table 指定表名,插入一行数据 * @param nullColumnHack 可选参数,建议是 null * 如果设置 null,将不允许向表中插入空数据,即 values = null 时无法正确执行插入操作。 * 如果不设置 null,那么需要设置表中可以为空的属性列的名称。 * 当 values = null 时,可以向表中插入空数据。 * 而实际上是插入一行数据,只有属性列名 nullColumnHack 的值是 null。 * @param values map 集合,包含需要插入的一行数据。至少需要包含一个属性的 key 和 value。 * key 是属性列名称,value 是属性值。 * @return 返回新插入的行序号, 发生错误时,返回 -1 */public long insert(String table, String nullColumnHack, ContentValues values) { try { return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE); } catch (SQLException e) { Log.e(TAG, "Error inserting " + values, e); return -1; }}
exp:
/** * SQL插入语句: * INSERT INTO tab(param1,param2,param3) VALUES ("param1","param2","param3"); */method 1:String sql = "INSERT INTO tab VALUES(?,?,?)";database.execSQL(sql,new String[]{"param1","param2","param3"});method 2:ContentValues contentValues = new ContentValues();contentValues.put("param1", "param1");contentValues.put("param2", "param2");contentValues.put("param3", "param3");/** * @第一参数 table 指定表名,插入一行数据 * @第二参数 nullColumnHack 可选参数,建议是 null * 如果设置 null,将不允许向表中插入空数据,即 values = null 时无法正确执行插入操作。 * 如果不设置 null,那么需要设置表中可以为空的属性列的名称。 * 当 values = null 时,可以向表中插入空数据。 * 而实际上是插入一行数据,只有属性列名 nullColumnHack 的值是 null。 * @第三参数 values map 集合,包含需要插入的一行数据。至少需要包含一个属性的 key 和 value。 * key 是属性列名称,value 是属性值。 * @return 返回新插入的行序号, 发生错误时,返回 -1 */database.insert("tab", null, contentValues);
删除数据
/** * 删除数据库中一行的方法 * * @param table 需要删除的表名 * @param whereClause 传入 null 时表示删除表中全部数据。 * 或者指定删除条件,只会删除满足条件的行。 * @param whereArgs 指定删除条件的值,按照顺序替换在删除条件中的 ? 。 * @return 删除满足条件的行时,返回删除的行数。找不到满足条件删除的时候,返回 0 。 */public int delete(String table, String whereClause, String[] whereArgs) { acquireReference(); try { SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table + (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs); try { return statement.executeUpdateDelete(); } finally { statement.close(); } } finally { releaseReference(); }}
exp:
/** *SQL删除语句: * DELETE FROM tab WHERE param1 = 'param1'; */method 1:String sql = "DELETE FROM tab WHERE param1 = ?";database.execSQL(sql,new String[]{"param1"});method 2:/** * @第一参数 table 需要删除的表名 * @第二参数 whereClause 传入 null 时表示删除表中全部数据。 * 或者指定删除条件,只会删除满足条件的行。 * @第三参数 whereArgs 指定删除条件的值,按照顺序替换在删除条件中的 ? 。 * @return 删除满足条件的行时,返回删除的行数。找不到满足条件删除的时候,返回 0 。 */database.delete("tab", "param1=?", new String[]{"param1"});
修改数据
/** * 更新数据库表中的一行数据 * * @param table 需要更新的表名 * @param values 包含属性名和新属性值的 map 集合。 * @param whereClause 可选的 WHERE 条件决定需要更新的行。 * 如果是空,则更新所有的行。 * @param whereArgs 替换在 where 条件中包含的 ? 。 * @return 返回更新的行数 */public int update(String table, ContentValues values, String whereClause, String[] whereArgs) { return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);}
exp:
/** * SQL更新语句: * UPDATE tab SET param1 = "newname" WHERE param1 = 'param1' ; */method 1:String sql = "UPDATE tab SET param1 = ? WHERE param1 = ?";database.execSQL(sql,new String[]{"newname", "param1"});method 2: ContentValues contentValues = new ContentValues();contentValues.put("param1", "newname");/** * @第一参数 table 需要更新的表名 * @第二参数 values 包含属性名和新属性值的 map 集合。 * @第三参数 whereClause 可选的 WHERE 条件决定需要更新的行。 * 如果是空,则更新所有的行。 * @第四参数 whereArgs 替换在 where 条件中包含的 ? 。 * @return 返回更新的行数 */database.update("tab", contentValues, "param1=?", new String[]{"param1"});
查询数据
/** * 查询数据库插数据 * * @param sql 所有的 query 方法,最后都会合并出 sql 执行 rawquery 方法 * @param selectionArgs 替换在 selection 中使用的 ? 或 sql 中使用的 ?。 * @return 返回Cursor(查询到的数据) */public Cursor rawQuery(String sql, String[] selectionArgs) { return rawQueryWithFactory(null, sql, selectionArgs, null, null);}
exp:
/** * SQL查询语句: * SELECT * FROM tab ; */String sql = "SELECT * FROM tab";Cursor cursor = database.rawQuery(sql, null);
四、事务处理
事务(Transaction)是一个对数据库执行工作单元。事务(Transaction)是以逻辑顺序完成的工作单位或序列,可以是由用户手动操作完成。SQLite 事务主要提供了三个 API。在结束事务前,如想将事务提交到数据库,需要设置事务完成标记。否则,在事务开启时候做的数据库操作将不会保留。
exp:
database.beginTransaction();....../* 此处执行数据库操作 */......database.setTransactionSuccessful();database.endTransaction();
简述查询返回的Cursor对象
cursor(游标)是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果
cursor常用方法介绍:1、moveToFirst():将指针移动到结果集的第一行;2、getColumnIndex():获取某一列在表中对应位置的索引;3、close():关闭指针。4、move(int offset):以当前位置为参考,移动到指定行5、moveToLast():移动到最后一行6、moveToPosition(int position):移动到指定行7、moveToPrevious():移动到前一行8、moveToNext():移动到下一行9、isFirst():是否指向第一条10、isLast():是否指向最后一条11、isBeforeFirst():/是否指向第一条之前12、isAfterLast():是否指向最后一条之后13、isNull(int columnIndex):指定列是否为空(列基数为0)14、isClosed():游标是否已关闭15、getCount():总数据项数16、getPosition():返回当前游标所指向的行数17、getString(int columnIndex):返回当前行指定列的值
exp:
Cursor cursor = database.rawQuery("select * from tab", null);while (cursor.moveToNext()) { String param1= cursor.getString(cursor.getColumnIndex("param1")); String param2= cursor.getString(cursor.getColumnIndex("param2")); String param3= cursor.getString(cursor.getColumnIndex("param3"));}cursor.close();
备注
Android数据库存储到此基本也介绍完了,其实这些都非常基础的数据库处理方式,如果你对Android原生的数据库创建方式感觉繁琐,其实你考虑使用第三方开源库。开源库在某种程度上简化了数据创建以及增删改查等功能。
到此Android的三大数据持久化存储(SharePreferences存储、文件存储、数据库存储)之数据库存储就介绍到这了。
更多相关文章
- Android (SQLite 数据库与ContentProvider)
- 无废话Android之android下junit测试框架配置、保存文件到手机内
- 王家林,云计算,大数据,Hadoop,Android,iOS,HTML5,Linux----王家林一站式
- Android XML解析学习——Dom方式
- Android 使用OpenCV的三种方式(Android Studio)
- Google公布Android数据,年增长250%,每天激活85万台Android设备
- 安卓软硬结合,热点技术实践总结:《Android音频口数据通信开发》 |
- android原生SQLite数据库的使用