安卓开发中,查询数据库是一项基础且核心的操作,以下是详细的实现步骤、代码示例及注意事项:
SQLite数据库的基本使用流程
- 创建/打开数据库:通过
SQLiteOpenHelper子类管理数据库版本升级和创建逻辑,例如定义一个继承自它的辅助类,重写onCreate()方法执行建表语句; - 获取可写或只读数据库实例:调用
getWritableDatabase()或getReadableDatabase()获得访问对象; - 执行SQL操作:使用
execSQL()直接运行DDL语句,或通过rawQuery()/query()方法进行数据检索; - 处理结果集:利用
Cursor对象遍历查询结果并提取字段值; - 关闭资源:及时释放Cursor和数据库连接以避免内存泄漏。
具体代码实现(含完整案例)
步骤1:定义数据库帮助类
public class MyDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "user_management.db"; // 数据库名称
private static final int VERSION = 1; // 版本号
// 构造函数,传入上下文环境
public MyDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createTableSQL = "CREATE TABLE IF NOT EXISTS users(" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT NOT NULL, " +
"age INTEGER, " +
"email TEXT UNIQUE);";
db.execSQL(createTableSQL); // 执行建表语句
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 简单策略:删除旧表后重建(实际项目建议迁移数据)
db.execSQL("DROP TABLE IF EXISTS users");
onCreate(db);
}
}
关键点解析:
onCreate仅在第一次安装时触发,而onUpgrade处理版本迭代时的结构调整,此处采用暴力重建方式,生产环境应考虑数据迁移方案。
步骤2:插入测试数据(可选)
// 在Activity或其他地方调用
MyDatabaseHelper helper = new MyDatabaseHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "张三");
values.put("age", 25);
values.put("email", "zhangsan@example.com");
db.insert("users", null, values); // 第三个参数为null时自动填充_id列
db.close(); // 记得关闭连接!
替代方案:也可以使用
execSQL插入多行记录,但ContentValues更适合单条数据的键值对映射。
步骤3:实现查询功能
以下是三种主流查询方式的对比实现:
| 方法类型 | 适用场景 | 优点 | 缺点 |
|—————-|——————————|———————–|———————|
| rawQuery() | 复杂条件拼接(如动态WHERE子句)| 支持参数化防注入 | SQL语法易出错 |
| query() | 标准化简单查询 | 类型安全,API清晰 | 难以表达复杂逻辑 |
| SQLite语句预编译| 高频重复执行相同结构查询 | 提升性能,减少解析开销| 代码稍显冗长 |
原生SQL+rawQuery()
String selectSql = "SELECT FROM users WHERE age > ? AND name LIKE ?";
String[] selectionArgs = {"20", "%李%"}; // 占位符的实际参数值
Cursor cursor = db.rawQuery(selectSql, selectionArgs);
while (cursor.moveToNext()) {
int idIndex = cursor.getColumnIndex("id");
String name = cursor.getString(cursor.getColumnIndex("name"));
int age = cursor.getInt(cursor.getColumnIndex("age"));
Log.d("DB_RESULT", "找到用户:" + name + ", 年龄:" + age);
}
cursor.close(); // 确保资源释放!
安全警示:永远不要直接拼接外部输入到SQL字符串中!必须使用问号占位符配合
selectionArgs数组来防止SQL注入攻击。
标准化API+query()
// 参数说明:表名、要获取的列数组、where条件、占位符参数、分组方式等
String[] columns = {"id", "name", "age"};
String selection = "age >= ?"; // WHERE age >= ?
String[] args = {"18"}; // 对应一个问号的位置参数
String groupBy = null; // 不需要分组时置空
String having = null; // HAVING子句同理
String orderBy = "age DESC"; // 按年龄降序排列
String limit = "5"; // 最多返回5条记录
Cursor cursor = db.query(
"users", // 目标表名
columns, // 需要返回的列集合
selection, // WHERE子句条件表达式
args, // 替换WHERE中的?的实际值数组
groupBy, // GROUP BY子句内容
having, // HAVING子句内容
orderBy, // 排序方式描述
limit // 分页限制数量
);
if (cursor != null) {
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndexOrThrow("id"));
String userName = cursor.getString(cursor.getColumnIndexOrThrow("name"));
// 其他字段类似处理...
}
cursor.close();
}
异常处理建议:当不确定某列是否存在时,优先用
getColumnIndexOrThrow()会抛出明确错误而非静默失败。
预编译语句提升效率
// 准备预处理语句对象
SQLiteStatement stmt = db.compileStatement(
"INSERT INTO users (name, age) VALUES (?, ?)");
// 绑定参数并执行多次插入
stmt.bindString(1, "王五");
stmt.bindLong(2, 30L);
stmt.executeInsert();
stmt.clearBindings(); // 清除当前绑定以便下次使用
stmt.bindString(1, "赵六");
stmt.bindLong(2, 28L);
stmt.executeInsert();
stmt.close(); // 最终关闭声明对象
性能优势:对于批量插入场景,预编译语句比原始SQL执行速度快约30%~50%。
最佳实践清单
- 事务批量操作:当需要进行多个相关修改时,启用事务保证原子性:
db.beginTransaction(); // 开启事务 try { // ...一系列数据库操作... db.setTransactionSuccessful(); //标记成功提交 } finally { db.endTransaction(); // 无论成败都要结束事务 } - 游标生命周期管理:始终在
finally块或try-with-resources中关闭Cursor,避免造成内存溢出; - 索引优化:对高频查询字段建立索引加速响应速度;
- 异步线程处理:将大数据量的读写放在后台线程执行,避免阻塞主线程导致ANR;
- 使用ORM框架简化开发:如Room库提供编译期校验的类型安全访问层,大幅降低手写SQL出错概率。
FAQs常见问题解答
Q1:为什么有时候查不到预期的数据?
A:可能原因包括:①未正确关闭之前的Cursor导致缓存不一致;②事务未提交即读取;③SQL条件表达式书写错误(特别是日期格式比较);④数据库文件被意外覆盖,建议开启日志输出完整的SQL语句进行调试。
Q2:如何查看手机上的实际数据库文件?
A:有两种方法:①通过Android Studio的Device File Explorer面板导航至/data/data/包名/databases路径导出文件;②使用第三方工具如Stetho库集成到应用内,或者连接真机后用adb pull命令拷贝到本地分析,注意调试版
