Android_SQLite数据库详解
1.SQLite概述
SQLite是一款轻量级的关系型数据库,由于它占用的资源非常少,所以在很多嵌入式设备都是用SQLite来存储数据。Android作为目前主流的移动操作系统,完全符合SQLite占用资源少的优势,故在Android平台上,集成了一个嵌入式关系型数据库SQLite。
继承SQLiteOpenHelper类,同时覆盖onCreate(SQLiteDatabase),onUpgrade(SQLiteDatabase,int,int)和定义构造函数,使用该类实例对象的getReadableDatabase()或者getWritableDatabase()得到当前数据库的实例化对象,可直接对其进行操作
2.SQLite实现增删改查
//数据库的基本操作,增删改查@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);DbOpenHelper dbOpenHelper = new DbOpenHelper(this, "mytest.db", 1);SQLiteDatabase db = dbOpenHelper.getWritableDatabase();//////////////////////////////////////////////////////////db.execSQL("insert into user(name, age) values(?,?)",new Object[]{"zhang", 10});//1.1增db.execSQL("delete from user where _id = ?",new String[]{"1"});//1.2删db.execSQL("update user set name=? where _id=?",new String[]{"haha", "2"});//1.3改Cursor cursor = db.rawQuery("select * from user where age >= ?", new String[]{"1"});//1.4查while(cursor.moveToNext()){Toast.makeText(this, cursor.getString(1), Toast.LENGTH_SHORT).show();}//////////////////////////////////////////////////////////ContentValues values = new ContentValues();//2.1增values.put("name", "John");values.put("age", 20);db.insert("user", //表格名"name", //String nullColumnHack//SQl不允许插入一个空行,当values的值为空时,将相当于执行于//insert into age(name) values("null");values);//插入值,相当于一个键值对的集合db.delete("user", "age>=? and age<=?", new String[]{"12", "18"});//2.2删ContentValues values1 = new ContentValues();//2.3改values.put("name", "liu");db.update("user", values1, "_id = ?", new String[]{"8"});Cursor cursor1 = db.query("user", //表格名//2.4查null, //String[] columns 如果为空,则返回所有列"age >= ?",//String selection 查询条件new String[]{"15"}, //String[] selectionArgs 查询参数null, //String groupBynull, //String havingnull);//String orderBywhile (cursor.moveToNext()) {int id = cursor1.getInt(cursor1.getColumnIndex("_id"));Toast.makeText(this, cursor1.getString(cursor1.getColumnIndex("name")), Toast.LENGTH_SHORT).show();}}/** * DbOpenHelper.java */import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;public class DbOpenHelper extends SQLiteOpenHelper{public DbOpenHelper(Context context, String name, int version) {super(context, name, null, version);}/** * 数据库只有在第一次创建时才调用该方法,对于一个已经存在的数据库,该方法不执行 * SQLiteDatabase.openOrCreateDatabase() */@Overridepublic void onCreate(SQLiteDatabase db) {String sql = "create table user(_id integer primary key autoincrement, name varchar(10), age integer)";db.execSQL(sql);}/** * 只有当数据库的版本号增加时,才会执行到该函数 * 初始默认数据库版本为0,当构造函数传递newVersion时,这时会有db.getVersion() 与 newVersion比较觉得该函数是否执行 */@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}}
3.getReadableDatabase()和getWritableDatabase()区别
(1).getReadableDatabase()
使用SQLiteDatabase实例对象的getReadableDatabase()方法,首先会检查数据库是否已经实例化并处于打开状态,如果满足要求直接返回数据库对象,否则试图返回一个可读写模式的数据库,但当磁盘空间已满,只能得到只读模式数据库对象
(2).getWritableDatabase()
使用SQLiteDatabase实例对象的getWritableDatabase()方法,目的是得到一个具有可读写的数据库实例对象,首先判断mDatabase是否不为空且已打开且不是只读模式,则直接返回该实例对象,否则如果mDatabase不为空则加锁,然后开始打开或创建数据库,进行版本相关操作并解锁,最后返回数据库实例对象!
总之,一般情况下,通过这两种方法获得的数据库实例对象时一样的,只有在数据库空间已满或其它情况下,通过getReadableDatabase得到的才是只读的实例对象
//得到一个具有可读写的数据库实例对象public synchronized SQLiteDatabase getWritableDatabase() {if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {return mDatabase;//如mDatabase不为空已打开并且不是只读模式 则返回该对象}if (mIsInitializing) {//判断是否正在初始化throw new IllegalStateException("getWritableDatabase called recursively");}boolean success = false;//默认为falseSQLiteDatabase db = null;if (mDatabase != null)mDatabase.lock();//如果mDatabase不为空则加锁 阻止其他的操作try {mIsInitializing = true;//初始化完成if (mName == null) {db = SQLiteDatabase.create(null);} else {db = mContext.openOrCreateDatabase(mName, 0, mFactory);//打开或创建数据库}int version = db.getVersion();//获取数据库版本(如果新创建的数据库版本为0)if (version != mNewVersion) {//获得的数据库版本号与指定的数据库版本号进行比较db.beginTransaction();//开始事务try {if (version == 0) {//新建的数据库版本号为0,则直接执行onCreate方法onCreate(db);} else {onUpgrade(db, version, mNewVersion);//如果版本号发生变化,且不是新建的数据库,则执行onUpgrade方法}db.setVersion(mNewVersion);//为当前的数据库设置新版本号db.setTransactionSuccessful();//设置事务成功提交} finally {db.endTransaction();//结束事务}}onOpen(db);success = true;return db;//返回当前的可读写模式数据库实例对象} finally {mIsInitializing = false;if (success) {if (mDatabase != null) {try {mDatabase.close();//如果mDatabase存在则先关闭} catch (Exception e) {}mDatabase.unlock();//为当前的数据库实例对象进行解锁操作}mDatabase = db;} else {if (mDatabase != null)mDatabase.unlock();if (db != null)db.close();}}}/* * 在getReadableDatabase()方法中,首先检查数据库是否已经实例化并处于打开状态 * 如果满足要求直接返回数据库对象,否则试图获取一个可读写模式的数据库实例; * 当磁盘空间已满,再以只读模式打开数据库 */public synchronized SQLiteDatabase getReadableDatabase() {if (mDatabase != null && mDatabase.isOpen()) {return mDatabase;//如果发现mDatabase存在并已打开,则直接返回该数据库对象}if (mIsInitializing) {//检查数据库是否正在进行初始化操作throw new IllegalStateException("初始化");}try {/******注意!!!调用了getWritableDatabase()方法*****/return getWritableDatabase();//一般情况下使用getReadableDatabase得到的是getWritableDatabase的数据库对象} catch (SQLiteException e) {if (mName == null)throw e; }/* * 使用可读写不能得到数据库对象,下面的操作只能得到对对象 * 常见数据库已满,不能再添加数据,此时只能写,不能读 */SQLiteDatabase db = null;try {mIsInitializing = true;String path = mContext.getDatabasePath(mName).getPath();db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);//以只读方式打开数据库if (db.getVersion() != mNewVersion) {//只读数据库,不接收新版本数据库throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to "+ mNewVersion + ": " + path);}onOpen(db);Log.w(TAG, "Opened " + mName + " in read-only mode");mDatabase = db;//mDatabase为打开的数据库对象return mDatabase;} finally {mIsInitializing = false;if (db != null && db != mDatabase)db.close();}}
4.Cursor对象
(1).主要方法
c.move(int offset);//以当前位置为参考,移动到指定行c.moveToFirst();//移动到第一行c.moveToLast();//移动到最后一行c.moveToPosition(int position);//移动到指定行c.moveToPrevious();//移动到前一行c.moveToNext();//移动到下一行c.isFirst();//是否指向第一条c.isLast();//是否指向最后一条c.isBeforeFirst();//是否指向第一条之前c.isAfterLast();//是否指向最后一条之后c.isNull(int columnIndex);//指定列是否为空(列基数为0)c.isClosed();//游标是否已关闭c.getCount();//总数据项数c.getPosition();//返回当前游标所指向的行数c.getColumnIndex(String columnName);//返回某列名对应的列索引值c.getString(int columnIndex);//返回当前行指定列的值(2).Cursor对象的管理
Activity提供了LoaderManager去管理cursor的生命周期,可参考cursor管理>>
(3).Cursor注意情况
1).要求
需要注意的是,在cursor的结果集中必须要包含一个“_id”的列,否则SimpleCursorAdapter会不识别,因为这源于SQLite的规范,主键以“_id”为标准。
2).措施
第一,建表时根据规范去做;
第二,查询时用别名,例如:SELECT id AS _id FROM person;
第三,使用CursorWrapperCursorWrapper cursorWrapper = new CursorWrapper(c) {@Overridepublic int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {if (columnName.equals("_id")) {return super.getColumnIndex("id");}return super.getColumnIndexOrThrow(columnName);}}
5.防止数据重复插入
@Overridepublic void onCreate(SQLiteDatabase db) {String sql = "create table bitmap(" +"_id integer primary key autoincrement," +/* * 防止重复,如果发现重复,数据不能添加 * fileName varchar(100) UNIQUE * * 如果是下面这样声明,重复冲突后,原有的数据将会被替换 * fileName varchar(100) UNIQUE ON CONFLICT REPLACE */" fileName varchar(100) UNIQUE," +" latitude varchar(100)," +" longitude varchar(100)," +" addr varchar(100)," +" isSaved integer,"+" model varchar(100))";db.execSQL(sql);}
6.事务Transacation
//数据库事务public void dbTransacation() {db.beginTransaction();//开始事务try {{//多个sql执行语句}db.setTransactionSuccessful(); //设置事务成功完成,将缓冲区的数据提交} finally {db.endTransaction(); //结束事务}}
7.关于CursorIndexOutOfBoundsException
在使用SQLiteDatabase.openOrCreateDatabase()去打开一个sdcard上的db文件时,此时光标应该移动为记录的最后,如果没有使用cursor.moveToFirst(),就会报该异常,所有任何游标使用时,应该先moveToFirst()
if (cursor != null && cursor.getCount() > 0) {cursor.moveToFirst();int indexTrainsetType = cursor.getColumnIndex(TrainsetTypeMetaDate.trainsetType);do {String trainsetType = cursor.getString(indexTrainsetType);list.add(trainsetType);} while (cursor.moveToNext());}
8.SQLITE分页查询
//方法一:select * from table_name where id > 7 limit 10 offset 3;limit 10 offset 3;//limit N表示取N个记录, offset P表示跳过P行.//方法二:select * from table_name where id > 7 limit 3,10;limit 3,10;//limit index,count表示跳过index行后,取出count个记录
9.Update or Replace
INSERT OR REPLACE INTO Employee (id, name, role) VALUES (1, 'John Foo', 'CEO');
BAD: This will insert or replace 2 of the columns... the NAME column will be set to NULL or the default value:
INSERT OR REPLACE INTO Employee (id, role) VALUES (1, 'code monkey');
GOOD: This will update 2 of the columns. When ID=1 exists, the NAME will be unaffected. When ID=1 does not exist, the name will be default (NULL).
INSERT OR REPLACE INTO Employee (id, role, name) VALUES ( 1, 'code monkey', (SELECT name FROM Employee WHERE id = 1) );
This will update 2 of the columns. When ID=1 exists, the ROLE will be unaffected. When ID=1 does not exist, the role will be set to 'Benchwarmer' instead of the default value.
INSERT OR REPLACE INTO Employee (id, name, role) VALUES ( 1, 'Susan Bar', COALESCE((SELECT role FROM Employee WHERE id = 1), 'Benchwarmer') );
10.Replace部分字符串
You can use the built in
replace()
function to perform a string replace in a query.Other string manipulation functions (and more) are detailed in theSQLite core functions list
The following should point you in the right direction.
UPDATE table SET field = replace( field, 'C:\afolder\', 'C:\anewfolder\' ) WHERE field LIKE 'C:\afolder\%'
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- Activity的启动模式和onNewIntent
- AIDL使用和Binder机制详解
- android 上传文件到服务器代码实例
- Android的存储系统—Vold与MountService分析(三)
- Android之ViewModel
- Android:SQLiteOpenHelper,SqliteDatabase学习
- Android中Widget实例
- Android(安卓)greenDAO 数据库 简单学习之基本使用