Android SQLiteDatabase类应用实践
一、SQLiteDatabase 基础概述
SQLite 是一款轻型的数据库,它是遵守 ACID 的关系型数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持 Windows/Linux/Unix 等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java 等,还有 Android 系统中同样内建了 SQLite 数据库。
在 Android 开发中,SQLiteDatabase
类是用于与 SQLite 数据库进行交互的核心类。它提供了一系列方法来执行 SQL 语句,管理数据库事务,以及进行数据的增删改查操作。
二、SQLiteDatabase 的获取与创建
在 Android 中,获取 SQLiteDatabase
实例通常通过 SQLiteOpenHelper
类的子类来实现。SQLiteOpenHelper
是一个抽象类,我们需要创建一个它的子类并实现其抽象方法。
2.1 创建 SQLiteOpenHelper 子类
以下是一个简单的 SQLiteOpenHelper
子类示例:
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class MyDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "my_database.db";
private static final int DATABASE_VERSION = 1;
public MyDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createTableQuery = "CREATE TABLE users (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"name TEXT," +
"age INTEGER)";
db.execSQL(createTableQuery);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 当数据库版本发生变化时,这里可以进行表的升级操作
db.execSQL("DROP TABLE IF EXISTS users");
onCreate(db);
}
}
在上述代码中:
- 构造函数中调用了
super(context, DATABASE_NAME, null, DATABASE_VERSION)
,其中context
是上下文对象,DATABASE_NAME
是数据库名称,null
表示使用默认的游标工厂,DATABASE_VERSION
是数据库版本号。 onCreate
方法在数据库首次创建时被调用,这里我们执行了一条 SQL 语句来创建一个名为users
的表,该表有id
(自增长主键)、name
(文本类型)和age
(整数类型)三个字段。onUpgrade
方法在数据库版本发生变化时被调用,这里简单地删除旧表并重新创建。
2.2 获取 SQLiteDatabase 实例
在 Activity 或其他组件中获取 SQLiteDatabase
实例:
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyDatabaseHelper dbHelper = new MyDatabaseHelper(this);
SQLiteDatabase db = dbHelper.getWritableDatabase();
if (db != null) {
Toast.makeText(this, "数据库获取成功", Toast.LENGTH_SHORT).show();
db.close();
} else {
Toast.makeText(this, "数据库获取失败", Toast.LENGTH_SHORT).show();
}
}
}
在上述代码中,通过 MyDatabaseHelper
的实例调用 getWritableDatabase
方法获取一个可写的 SQLiteDatabase
实例。如果获取成功,会弹出提示“数据库获取成功”,否则提示“数据库获取失败”。获取到的数据库使用完毕后,应该调用 close
方法关闭,以释放资源。
三、数据插入操作
SQLiteDatabase 提供了多种方法来插入数据,包括 insert
、insertOrThrow
和 execSQL
等。
3.1 使用 insert 方法
insert
方法的语法如下:
long insert(String table, String nullColumnHack, ContentValues values)
table
:要插入数据的表名。nullColumnHack
:如果ContentValues
为空,为了确保插入操作能够正确执行,需要指定一个列名。通常设置为null
。ContentValues
:一个键值对集合,用于存储要插入的数据。键是列名,值是对应的数据。
以下是插入数据的示例:
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyDatabaseHelper dbHelper = new MyDatabaseHelper(this);
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "John");
values.put("age", 30);
long newRowId = db.insert("users", null, values);
if (newRowId != -1) {
Toast.makeText(this, "数据插入成功,行 ID: " + newRowId, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "数据插入失败", Toast.LENGTH_SHORT).show();
}
db.close();
}
}
在上述代码中,我们创建了一个 ContentValues
对象,并向其中添加了 name
和 age
的值。然后调用 db.insert
方法将数据插入到 users
表中。如果插入成功,insert
方法会返回新插入行的 ID,否则返回 -1。
3.2 使用 insertOrThrow 方法
insertOrThrow
方法与 insert
方法类似,不同之处在于如果插入操作失败,insertOrThrow
会抛出异常,而 insert
只会返回 -1。
long insertOrThrow(String table, String nullColumnHack, ContentValues values)
使用示例如下:
try {
long newRowId = db.insertOrThrow("users", null, values);
Toast.makeText(this, "数据插入成功,行 ID: " + newRowId, Toast.LENGTH_SHORT).show();
} catch (SQLException e) {
Toast.makeText(this, "数据插入失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
通过 try - catch
块捕获可能抛出的 SQLException
,并在捕获到异常时显示错误信息。
3.3 使用 execSQL 方法
execSQL
方法可以执行任何 SQL 语句,包括插入语句。使用 execSQL
插入数据的示例如下:
String insertQuery = "INSERT INTO users (name, age) VALUES ('Jane', 25)";
db.execSQL(insertQuery);
Toast.makeText(this, "数据插入成功", Toast.LENGTH_SHORT).show();
这种方式直接执行 SQL 语句进行插入操作,但需要手动编写 SQL 语句,相比 insert
和 insertOrThrow
方法,代码的可读性和安全性略差,因为容易出现 SQL 注入问题。
四、数据查询操作
SQLiteDatabase 提供了 query
方法来执行查询操作,它的语法较为复杂,以满足不同的查询需求。
4.1 基本查询
query
方法的基本语法如下:
Cursor query(String table, String[] columns, String selection, String[] selectionArgs,
String groupBy, String having, String orderBy)
table
:要查询的表名。columns
:要返回的列名数组,如果设置为null
,则返回所有列。selection
:查询条件,类似于 SQL 语句中的WHERE
子句,但不包含WHERE
关键字。selectionArgs
:用于替换selection
中的占位符的值数组。groupBy
:分组条件,类似于 SQL 语句中的GROUP BY
子句。having
:分组后的过滤条件,类似于 SQL 语句中的HAVING
子句。orderBy
:排序条件,类似于 SQL 语句中的ORDER BY
子句。
以下是一个基本查询的示例,查询 users
表中所有数据:
MyDatabaseHelper dbHelper = new MyDatabaseHelper(this);
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.query("users", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
int id = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
int age = cursor.getInt(cursor.getColumnIndex("age"));
Toast.makeText(this, "ID: " + id + ", Name: " + name + ", Age: " + age, Toast.LENGTH_SHORT).show();
} while (cursor.moveToNext());
}
cursor.close();
db.close();
在上述代码中,通过 db.query
方法查询 users
表中的所有数据。cursor.moveToFirst()
方法将游标移动到结果集的第一行,如果结果集不为空,则通过 do - while
循环遍历结果集,使用 cursor.getColumnIndex
方法获取列的索引,然后通过列索引获取对应列的值。最后使用 cursor.close()
关闭游标,释放资源。
4.2 带条件查询
以下是查询 users
表中年龄大于 25 岁的用户的示例:
String selection = "age >?";
String[] selectionArgs = {"25"};
Cursor cursor = db.query("users", null, selection, selectionArgs, null, null, null);
if (cursor.moveToFirst()) {
do {
int id = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
int age = cursor.getInt(cursor.getColumnIndex("age"));
Toast.makeText(this, "ID: " + id + ", Name: " + name + ", Age: " + age, Toast.LENGTH_SHORT).show();
} while (cursor.moveToNext());
}
cursor.close();
db.close();
在上述代码中,selection
设置为 age >?
,selectionArgs
设置为 {"25"}
,表示查询年龄大于 25 岁的用户。
4.3 分组和排序查询
以下是查询 users
表中按年龄分组,并统计每个年龄组的人数,按人数降序排列的示例:
String groupBy = "age";
String having = "COUNT(*) > 1";
String orderBy = "COUNT(*) DESC";
Cursor cursor = db.query("users", new String[]{"age", "COUNT(*) AS count"}, groupBy, null, groupBy, having, orderBy);
if (cursor.moveToFirst()) {
do {
int age = cursor.getInt(cursor.getColumnIndex("age"));
int count = cursor.getInt(cursor.getColumnIndex("count"));
Toast.makeText(this, "Age: " + age + ", Count: " + count, Toast.LENGTH_SHORT).show();
} while (cursor.moveToNext());
}
cursor.close();
db.close();
在上述代码中,groupBy
设置为 age
,表示按年龄分组;having
设置为 COUNT(*) > 1
,表示只显示人数大于 1 的分组;orderBy
设置为 COUNT(*) DESC
,表示按人数降序排列。
五、数据更新操作
SQLiteDatabase 提供了 update
方法来更新表中的数据。
5.1 update 方法语法
int update(String table, ContentValues values, String whereClause, String[] whereArgs)
table
:要更新数据的表名。values
:一个ContentValues
对象,包含要更新的列名和新值。whereClause
:更新条件,类似于 SQL 语句中的WHERE
子句,但不包含WHERE
关键字。whereArgs
:用于替换whereClause
中的占位符的值数组。
5.2 更新数据示例
以下是将 users
表中 name
为 John
的用户的年龄更新为 35 的示例:
MyDatabaseHelper dbHelper = new MyDatabaseHelper(this);
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("age", 35);
String whereClause = "name =?";
String[] whereArgs = {"John"};
int rowsUpdated = db.update("users", values, whereClause, whereArgs);
if (rowsUpdated > 0) {
Toast.makeText(this, "数据更新成功,更新行数: " + rowsUpdated, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "数据更新失败", Toast.LENGTH_SHORT).show();
}
db.close();
在上述代码中,首先创建一个 ContentValues
对象,设置要更新的 age
字段的值。然后设置更新条件 whereClause
为 name =?
,并通过 whereArgs
提供占位符的值。调用 db.update
方法执行更新操作,返回值 rowsUpdated
表示更新的行数。如果更新行数大于 0,则表示更新成功。
六、数据删除操作
SQLiteDatabase 提供了 delete
方法来删除表中的数据。
6.1 delete 方法语法
int delete(String table, String whereClause, String[] whereArgs)
table
:要删除数据的表名。whereClause
:删除条件,类似于 SQL 语句中的WHERE
子句,但不包含WHERE
关键字。whereArgs
:用于替换whereClause
中的占位符的值数组。
6.2 删除数据示例
以下是删除 users
表中 age
大于 30 的用户的示例:
MyDatabaseHelper dbHelper = new MyDatabaseHelper(this);
SQLiteDatabase db = dbHelper.getWritableDatabase();
String whereClause = "age >?";
String[] whereArgs = {"30"};
int rowsDeleted = db.delete("users", whereClause, whereArgs);
if (rowsDeleted > 0) {
Toast.makeText(this, "数据删除成功,删除行数: " + rowsDeleted, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "数据删除失败", Toast.LENGTH_SHORT).show();
}
db.close();
在上述代码中,设置删除条件 whereClause
为 age >?
,并通过 whereArgs
提供占位符的值。调用 db.delete
方法执行删除操作,返回值 rowsDeleted
表示删除的行数。如果删除行数大于 0,则表示删除成功。
七、数据库事务处理
数据库事务是一组操作的集合,这些操作要么全部成功执行,要么全部不执行,以确保数据的一致性和完整性。在 SQLiteDatabase 中,可以通过 beginTransaction
、setTransactionSuccessful
和 endTransaction
方法来管理事务。
7.1 事务操作示例
以下是一个简单的事务操作示例,将两个用户的数据插入到 users
表中:
MyDatabaseHelper dbHelper = new MyDatabaseHelper(this);
SQLiteDatabase db = dbHelper.getWritableDatabase();
try {
db.beginTransaction();
ContentValues values1 = new ContentValues();
values1.put("name", "Alice");
values1.put("age", 28);
db.insert("users", null, values1);
ContentValues values2 = new ContentValues();
values2.put("name", "Bob");
values2.put("age", 32);
db.insert("users", null, values2);
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
db.close();
}
在上述代码中,通过 db.beginTransaction()
开始一个事务,在事务中执行两个插入操作。如果所有操作都成功执行,调用 db.setTransactionSuccessful()
标记事务成功。最后在 finally
块中调用 db.endTransaction()
结束事务。如果事务过程中发生异常,由于没有标记事务成功,endTransaction
方法会回滚事务,确保数据的一致性。
八、SQLiteDatabase 的优化与注意事项
- 避免频繁打开和关闭数据库:每次打开和关闭数据库都有一定的开销,尽量在需要使用数据库的时间段内保持数据库连接打开,使用完毕后再关闭。
- 使用事务:如前面所述,使用事务可以确保数据的一致性和完整性,同时也能提高批量操作的效率。
- 合理使用索引:在经常用于查询条件的列上创建索引可以显著提高查询性能,但索引也会占用额外的存储空间,并且会增加插入、更新和删除操作的时间,所以要根据实际情况权衡。
- 避免 SQL 注入:使用
selectionArgs
等方式来传递参数,而不是直接将用户输入拼接到 SQL 语句中,以防止 SQL 注入攻击。 - 及时释放资源:在使用完
SQLiteDatabase
、Cursor
等资源后,要及时调用close
方法释放资源,避免内存泄漏。
通过以上对 SQLiteDatabase 在 Android 应用中的详细实践介绍,开发者可以更加熟练地运用 SQLite 数据库来管理应用数据,提高应用的性能和稳定性。在实际开发中,应根据具体需求和场景,合理选择和使用 SQLiteDatabase 的各种方法和特性,以实现高效的数据管理。