1. 数据类型
与其它数据库不同,SQLite的数据类型很简单,只有NULL(空类型)、INTEGER(整型)、REAL(浮点型)、TEXT(字符串型)、BLOB(二进制型)。
SQLite为动态数据类型(弱引用),当向数据库中插入某个值时,会检查该值的类型,若类型与插入列不匹配,SQLite会尝试将该值转换成相应类型。
2. 创建数据库
如果对SQL的操作不了解,建议先看看这一块:MySQL入门笔记(一)
Android中提供了一个SQLiteOpenHelper类,使用该类可非常方便地进行数据库的创建与升级。
SQLiteOpenHelper是一个抽象类,必须实现onCreate()、onUpgrade(),这两个方法分别用于数据库的创建与升级,onCreate()在数据库不存在的情况下会调用,而onUpgrade()则是当版本号高于已有数据库时调用。
SQLiteOpenHelper构造函数的第一个参数是context,第二个参数是数据库的名称,第三个参数允许在查询数据时返回一个自定义的Cursor,直接传入null即可,第四个参数是数据库版本号,用于数据库的升级操作。
调用getReadableDtabase()或者getWritableDatabase()方法即可创建或打开数据库,同时返回一个SQLiteDatabase对象,用于操作数据库。这里,SQLiteDatabase对象有两个重要方法,exexSQL方法和rawQuery方法,前者用于执行SQL语句,后者用于查询记录,后面会具体介绍。
拓展:Context与SQLiteDatabase的方法openOrCreateDatabase的区别 (context、SQLiteOpenHelper)
范例:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new SQLiteOpenHelper(this, "Library.db", null, 1) { static final String CREATE_BOOK = "create table BOOK (" + "id integer primary key autoincrement, " + "name text, " + "author text)"; @Override public void onCreate(SQLiteDatabase db) { // 创建数据表 db.execSQL(CREATE_BOOK); Toast.makeText(MainActivity.this, "创建成功!", Toast.LENGTH_SHORT).show(); } // 用于升级数据库 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }.getWritableDatabase(); } }
3. 升级数据库
在进行app的迭代时,往往也会伴随着进行数据库的升级,这就需要通过onUpgrade()方法实现。需要升级数据库时,在实例化SQLiteOpenHelper时传入一个更高的版本号即可。
范例:
假定进行两次迭代:1. 新增一张表catelogy,2. 给表book增加category_id字段。
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new SQLiteOpenHelper(this, "Library.db", null, 1) { static final String CREATE_BOOK = "create table BOOK (" + "id integer primary key autoincrement, " + "name text, " + "author text," + "category_id integer)";//第二次升级时增加 static final String CREATE_CATEGORY = "create table CATEGORY (" + "id integer primary key autoincrement, " + "category_name text," + "category_code integer)"; static final String CHANGE_COLUMN = "alter table BOOK column category_id integer"; @Override public void onCreate(SQLiteDatabase db) { // 如果用户当前是初次安装本程序,即数据库未被创建,则直接创建两个表 db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); Toast.makeText(MainActivity.this, "创建成功!", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch (oldVersion) { // 不使用break case 1: db.execSQL(CREATE_CATEGORY); case 2: db.execSQL(CHANGE_COLUMN); default: } } }.getWritableDatabase(); } }
这里注意一个细节,onUpgrade()方法中的switch语句里的并未使用break,这是为了保证在跨版本升级时能顺利依次完成中间的所有升级。
4. 数据操作
4.1 execSQL()和rawQuery()
上面提到,调用getReadableDatabase()或者getWritableDatabase()方法时会返回一个SQLiteDatabase对象,要对数据进行操作,就是使用这一对象。
结合上面的例子,我们可以看到SQLiteDatabase提供了一个调用SQL语句的方法execSQL(),通过这个方法可以调用除查询语句以外的SQL语句,同时SQLiteDatabase类提供了一个rawQuery()方法,用于查询数据。
(1)execSQL()
execSQL(String sql) execSQL(String sql, Object[] bingArgs)
使用execSQL()方法传入参数有两种方式,第一种直接传入SQL语句,如有参数则直接拼接在一起,例如:
String delete_sql = "delete from test where _id = " + idString; db.execSQL(sql);
第二种方法则是在参数的位置插入占位符?,而后将具体参数传给execSQL()方法的第二个参数,如有多个参数,则依次放入:
String delete_sql = "delete from test where _id = ?, name = ?"; db.execSQL(sql, new String[] {idString, nameString});
(2)rawQuery()
rawQuery(String sql, String[] selectionArgs)
rawQuery()方法的使用与execSQL()的第二种用法基本一致。
4.2 安卓提供的API
除了execSQL()方法和rawQuery()方法,安卓还针对数据库的CRUD操作封装了API,更加便于操作。
(1)insert()
insert(String table, String nullColumnHack, ContentValues values)
table
:表名。
nullColumnHack
:若某些允许为空的列没有赋值,则自动赋值为null,传入null即可。
values
:ContentValues对象,相当于一个容器的作用,可通过它的put(key, values)方法(key为列名称,values为该列的值)将每一行的数据一一添加,而后再传入数据库。如需插入多条记录,插入下一条记录前要调用clear()方法进行清空。
返回一个id值,即插入记录的id。
范例:
//获取SQLiteDatabase对象 db = mHelper.getWritableDatabase(); //实例化ContentValues对象并进行数据组装 ContentValues values = new ContentValues(); values.put("name", "A"); values.put("author", "aa"); //插入数据 db.insert("BOOK", null, values); //若需要插入多条记录,在这一步一定要调用values的clear()方法清空现有的数据 values.clear(); values.put("name", "B"); values.put("author", "bb"); db.insert("BOOK", null, values);
(2)delete()
delete(String table, String whereClause, String[] whereArgs)
whereClause
和whereArgs
:指定删除条件,若不指定则删除表中所有数据。即SQL中where字句部分,占位符的使用同execSQL()与rawQuery(),例如将age > 30的记录删除,则第二个参数为"age > ?",第三个参数为new String[] {"30"}。
范例:
//删除数据,?是一个占位符,第二、第三个参数合起来即删除id > 2的记录 db.delete("BOOK", "id > ?", new String[] {"2"});
(3)update()
update(String table, ContentValues values, String whereClause, String[] whereArgs)
values同Insert()方法,将更新后的数据通过ContentValues对象组装,而whereClause和whereArgs则同delete()方法,指定更新哪些记录。
范例:
ContentValues values = new ContentValues(); values.put("name", "D"); db.update("BOOK", values, "id < ?", new String[] {"3"});
(4)query()
query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
如果学过SQL语言的话,相信对上面这一长串的参数是比较容易理解的,基本上可以与SQL中select语句的结构对应上。调用query()方法会返回一个Cursor对象,里面含有所有查询结果。
table
:对应SQL中的from table_name
,指定查询的表名。
columns
:对应select column1, column2, ...
,指定查询的列名称。
selection
:对应where column = value
,指定查询的行的约束条件。
selectionArgs
:为selection中的占位符(?)提供具体值。
groupBy
:对应group by column
,指定对哪一列进行分组。
having
:对应having column = value
,进一步对分组后的结果进行约束。
orderBy
:对应order by column1, column2, ...
,指定查询结果的排序方式。
范例:
db = mHelper.getWritableDatabase(); //查询BOOK中所有数据 Cursor cursor = db.query("BOOK", null, null, null, null, null, null); if (cursor.moveToFirst()) { do { //遍历cursor,取出数据 int id = cursor.getInt(cursor.getColumnIndex("id")); String name = cursor.getString(cursor.getColumnIndex("name")); String author = cursor.getString(cursor.getColumnIndex("author")); //打印数据 Log.d("MySQLiteData", id + ". 《" + name + "》 " + author); }while (cursor.moveToNext()); //关闭Cursor对象 cursor.close(); }
5. 事务
SQLite是支持事务的,那么什么是事务呢?假定需要更新大量数据,就要先删除旧数据,而后再插入新数据。但是在过程中出现了异常,旧数据被删除之后,新数据没有成功插入,那这一个数据表的信息就全没了。
事务的特性就是保证某一个系列的操作,或者全部完成,或者全部不完成。如果替换数据放在事务中进行,则要么数据替换成功,表内全部换成新数据,要么失败,保留旧数据,不会出现删了旧数据,但是没插入新数据的情况。
事务的使用方法非常简单:
// 通过SQLiteDatabase对象开启事务 db.beginTransaction(); try { // 在异常捕获的代码块中进行具体的操作 // 在全部操作结束后将事务设置为成功 db.setTransactionSuccessful(); } catch (Exception e) { e.printStackTrace(); } finally ( // 结束事务 db.endTransaction(); )