32.Android SQLite 及其更新

  • 32Android SQLite 及其更新
    • Android SQLite 介绍
    • Android SQLite 继承SQLiteOpenHelper
    • Android SQLite 创建表
    • Android SQLite 增
    • Android SQLite 删
    • Android SQLite 改
    • Android SQLite 查
    • Android SQLite 删除表
    • Android SQLite 代码总览
    • Android SQLite 更新表的重要性
    • Android SQLite 更新表
    • Android SQLite 不要close


Android SQLite 介绍

Android上SQLite用起来,相比IOS的SQLite,显得麻烦一些。

SQLite最大的问题就是表的维护(表的升级:添加字段等等)。

这次也是着重谈谈SQLite的维护。


Android SQLite 继承SQLiteOpenHelper

extends SQLiteOpenHelper

定义一个子类继承抽象类SQLiteOpenHelper


Android SQLite 创建表

在子类的的onCreate方法里执行对应的sql语句。

    public static final String TB_CAMNTER_SQL = "CREATE TABLE IF NOT EXISTS " + TB_CAMNTER + "(_id  INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +            " content text)";    /**     * Called when the database is created for the first time. This is where the     * creation of tables and the initial population of the tables should happen.     * 首次创建数据库时调用。这就是创建表和表的初始种群。     *     * @param db The database.     */    @Override    public void onCreate(SQLiteDatabase db) {        db.execSQL(TB_CAMNTER_SQL);    }            

Android SQLite 增

    /**     * 保存数据     *     * @param content     */    public void insert(String content) {        SQLiteDatabase insert = this.getWritableDatabase();        insert.beginTransaction();        ContentValues values = new ContentValues();        values.put("content", content);        insert.insert(TB_CAMNTER, "", values);        /**         * 设置事务处理成功,不设置会自动回滚不提交         */        insert.setTransactionSuccessful();        /**         * 处理完成         */        insert.endTransaction();    }

Android SQLite 删

    /**     * 删除数据     */    public void deleteAll() {        SQLiteDatabase deleteAll = this.getWritableDatabase();        deleteAll.delete(TB_CAMNTER, null, null);        deleteAll.close();    }

Android SQLite 改

    /**     * 修改第一条数据     */    public void updateFirst() {        List allData = this.queryAll();        if (allData == null || allData.size() < 1) return;        int firstId = allData.get(0).id;        SQLiteDatabase update = this.getWritableDatabase();        update.beginTransaction();        ContentValues values = new ContentValues();        values.put("content", UUID.randomUUID().toString());        update.update(TB_CAMNTER, values, "_id=?", new String[]{firstId + ""});        update.setTransactionSuccessful();        update.endTransaction();    }

Android SQLite 查

    /**     * 查询数据     *     * @return     */    public List queryAll() {        List allData = new ArrayList<>();        SQLiteDatabase queryAll = this.getReadableDatabase();        queryAll.beginTransaction();        String sql = "select * from " + TB_CAMNTER;        Cursor result = queryAll.rawQuery(sql, null);        for (result.moveToFirst(); !result.isAfterLast(); result.moveToNext()) {            SQLiteData data = new SQLiteData();            data.id = result.getInt(result.getColumnIndex("_id"));            data.content = result.getString(result.getColumnIndex("content"));            allData.add(data);        }        queryAll.setTransactionSuccessful();        queryAll.endTransaction();        result.close();        return allData;    }

Android SQLite 删除表

    /**     * 删除表     */    public void deleteTable() {        SQLiteDatabase delete = this.getWritableDatabase();        delete.execSQL("DROP TABLE " + TB_CAMNTER);    }

Android SQLite 代码总览

MySQLiteHelper

public class MySQLiteHelper extends SQLiteOpenHelper {    private static final String DB_NAME = "camnter.db";    private static final int VERSION = 1;    private static final String TB_CAMNTER = "tb_camnter";    public static final String TB_CAMNTER_SQL = "CREATE TABLE IF NOT EXISTS " + TB_CAMNTER + "(_id  INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +            " content text)";    public static MySQLiteHelper ourInstance;    public MySQLiteHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {        super(context, name, factory, version);    }    private MySQLiteHelper(Context context) {        this(context, DB_NAME, null, VERSION);    }    public static MySQLiteHelper getInstance(Context context) {        if (ourInstance == null) ourInstance = new MySQLiteHelper(context);        return ourInstance;    }    /**     * Called when the database is created for the first time. This is where the     * creation of tables and the initial population of the tables should happen.     * 首次创建数据库时调用。这就是创建表和表的初始种群。     *     * @param db The database.     */    @Override    public void onCreate(SQLiteDatabase db) {        db.execSQL(TB_CAMNTER_SQL);    }    /**     * Called when the database needs to be upgraded. The implementation     * should use this method to drop tables, add tables, or do anything else it     * needs to upgrade to the new schema version.     * 数据库需要更新时调用,实现类应该使用这个方法删除表,添加表,或者做其他事情需要更新表到新模式版本     * 

*

* The SQLite ALTER TABLE documentation can be found * here. If you add new columns * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns * you can use ALTER TABLE to rename the old table, then create the new table and then * populate the new table with the contents of the old table. * 如果你添加新的列你可以使用ALTER TABLE插入到现存的表里。如果你想重命名或移除列你可以使用ALTER TABLE重命名 * 旧表,然后创建新表,接着填充新表与旧表的内容 *

* This method executes within a transaction. If an exception is thrown, all changes * will automatically be rolled back. * 这个方法执行了一个事务。如果抛出一个异常,所有更改将自动回滚。 *

* * @param db The database. 数据库 * @param oldVersion The old database version. 旧数据库版本 * @param newVersion The new database version. 新数据库版本 */
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion == 1 && newVersion == 2) { String sql = "ALTER TABLE " + TB_CAMNTER + " ADD " + "status" + " INT default " + 0; db.execSQL(sql); } } /** * 删除表 */ public void deleteTable() { SQLiteDatabase delete = this.getWritableDatabase(); delete.execSQL("DROP TABLE " + TB_CAMNTER); } /** * 保存数据 * * @param content */ public void insert(String content) { SQLiteDatabase insert = this.getWritableDatabase(); insert.beginTransaction(); ContentValues values = new ContentValues(); values.put("content", content); insert.insert(TB_CAMNTER, "", values); /** * 设置事务处理成功,不设置会自动回滚不提交 */ insert.setTransactionSuccessful(); /** * 处理完成 */ insert.endTransaction(); } /** * 删除数据 */ public void deleteAll() { SQLiteDatabase deleteAll = this.getWritableDatabase(); deleteAll.delete(TB_CAMNTER, null, null); deleteAll.close(); } /** * 修改第一条数据 */ public void updateFirst() { List allData = this.queryAll(); if (allData == null || allData.size() < 1) return; int firstId = allData.get(0).id; SQLiteDatabase update = this.getWritableDatabase(); update.beginTransaction(); ContentValues values = new ContentValues(); values.put("content", UUID.randomUUID().toString()); update.update(TB_CAMNTER, values, "_id=?", new String[]{firstId + ""}); update.setTransactionSuccessful(); update.endTransaction(); } /** * 查询数据 * * @return */ public List queryAll() { List allData = new ArrayList<>(); SQLiteDatabase queryAll = this.getReadableDatabase(); queryAll.beginTransaction(); String sql = "select * from " + TB_CAMNTER; Cursor result = queryAll.rawQuery(sql, null); for (result.moveToFirst(); !result.isAfterLast(); result.moveToNext()) { SQLiteData data = new SQLiteData(); data.id = result.getInt(result.getColumnIndex("_id")); data.content = result.getString(result.getColumnIndex("content")); allData.add(data); } queryAll.setTransactionSuccessful(); queryAll.endTransaction(); result.close(); return allData; }}

Android SQLite 更新表的重要性

正常情况,如果涉及到添加表的新字段,那么可以调用上面的删除表操作:

    /**     * 删除表     */    public void deleteTable() {        SQLiteDatabase delete = this.getWritableDatabase();        delete.execSQL("DROP TABLE " + TB_CAMNTER);    }

我记得有这么一种情况,不得不得更新表,不能删除表:

是一款关于健身类的应用,用户做的运动都会记录下来,包括在没用网的时候,运动记录都会记录在本地,每次打开App都会判断有无网的情况,上传本地数据给服务端。假如,用户在没用网的时候运动了,并且保存了运动记录在本机,在没用网的情况下,运动记录是上传不到服务端的。这时,用户下载了新版App准备安装,你又不能在新版本App中删除本地数据库,而只能做兼容操作(数据库更新)。不然,会造成数据的流失,这就是包含离线版本的App要考虑的事情。


Android SQLite 更新表

刚才用上述的MySQLiteHelper里的代码可以生成这样的表:

然后,我们将MySQLiteHelper里的:

private static final int VERSION = 1;

改为

private static final int VERSION = 2;

就会走到onUpgrade里:

在这里我们可以判断是从旧的哪个版本更到新的哪个版本里(比如版本1到2需要添加一个字段,版本2到3需要两个字段。那么,版本1-3呢?这时,就需要2个字段了。所以,要对应oldVersion和newVersion的情况重构表)。

    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        if (oldVersion == 1 && newVersion == 2) {            String sql = "ALTER TABLE " + TB_CAMNTER + " ADD " + "status" + " INT default " + 0;            db.execSQL(sql);        }    }

更新表后,执行任何(增、删、改、查)操作,我们再打开数据库是这样的:

此时,看到了新字段status,就代表更新表成功了。同时,也设置上了默认值,也对应了上面onUpgrade里的SQL语句的字段default 0


Android SQLite 不要close

除了Cursor意外,都不要在操作完后执行close()

原因: 通过getReadableDatabase()或者getWritableDatabase()都是创建一个数据库的连接放入SQLite的连接池里(可以查看SQLiteDatabase里的SQLiteConnectionPool)。但是如果在多线程中,你每次操作前都打开SQLite连接,执行完操作后又close了连接,就会出现这样的一个问题:

  • 1.多线程中,写入数据到数据库,因为多线程会创建不同的连接,在同一时间里,其中线程创建连接时将会失败:

android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

  • 2.多线程中,可能线程1关闭了数据库,然而线程2还在使用数据库,这时候就会崩溃了:

Leak found
Caused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed


相应的,也有解决办法:

  • 1.除了Cursor以外,都不要close(我认为最好的,因为SQLiteDatabase有连接池的概念)。

  • 2.就是在涉及在数据库多线程操作的方法写成 synchronized 方法。

Github 是上也有类似问题的分析

更多相关文章

  1. java服务端与ios,android实现简单数据加密工具
  2. Android(安卓)TabHost的使用(二)
  3. Android(安卓)中GridView上图下字、GridView显示文字
  4. Android基于Intent实现Activity之间数据传递的方法
  5. Android(安卓)Intent Action 大全
  6. 使用ContentProvider管理照片
  7. android音频波形图绘制
  8. android java.util.Date和java.util.sql中Date的区别
  9. Android返回数据给上一个活动-startActivityForResult

随机推荐

  1. Android应用程序键盘(Keyboard)消息处理机
  2. android linux sdk 纯命令行式升级
  3. android service中显示一个dialog
  4. android kotlin 基础二
  5. Android(安卓)SimpleDateFormat 日期时间
  6. Android(安卓)ButterKnife8.2.1 使用
  7. 数据结构基础知识核心归纳(二)
  8. Android中Toolbar的使用
  9. Android(安卓)X迁移
  10. Android(安卓)NDK: WARNING: APP_PLATFOR