32.Android(安卓)SQLite 及其更新
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 是上也有类似问题的分析
更多相关文章
- java服务端与ios,android实现简单数据加密工具
- Android(安卓)TabHost的使用(二)
- Android(安卓)中GridView上图下字、GridView显示文字
- Android基于Intent实现Activity之间数据传递的方法
- Android(安卓)Intent Action 大全
- 使用ContentProvider管理照片
- android音频波形图绘制
- android java.util.Date和java.util.sql中Date的区别
- Android返回数据给上一个活动-startActivityForResult