SQLite的query方法,注意检查参数args中是否含null值

本文分析了Android应用中使用SQLite查询时出现的NullPointerException问题,并详细解释了导致此问题的原因及解决方案。
Android 2.3.3   
Eclipse Version: 3.7.0   
LogCat 

 LogCat  报错信息:

03-13 10:25:37.163: ERROR/AndroidRuntime(4166): java.lang.IllegalArgumentException: the bind value at index 2 is null  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at android.database.sqlite.SQLiteProgram.bindString(SQLiteProgram.java:237)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at android.database.sqlite.SQLiteQuery.bindString(SQLiteQuery.java:185)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:48)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1356)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1235)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1189)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1271)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at com.tmall.nokia.db.RecordDBHelper.query(RecordDBHelper.java:53)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at com.tmall.nokia.manage.RecordDBopt.get(RecordDBopt.java:80)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at com.tmall.nokia.Play.savePlayCount(Play.java:775)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at com.tmall.nokia.Play$5.run(Play.java:369)  
03-13 10:25:37.163: ERROR/AndroidRuntime(4166): at java.util.Timer$TimerImpl.run(Timer.java:284) 

 

发生错误原因分析:

检查相关代码,发现

传入savePlayCount的String值programid为null,在传入之前和传入后均未作判断

public void savePlayCount(String programid, int count) {  
……  
}  

 

而programid最终作为参数args这个String数组中的一个值,用于数据库查询

Cursor cur = db.query(TBL_NAME, null, where, args, null, null, order);  

 

由于args这个String数组中的String有null值,报错

 

解决办法:

在传入programid前或之后对其值作判断

if (programid != null) {  
……  
}

 

PS:args这个String数组中不能有null值

<think>我们正在讨论的是Android中的Cursor query方法。用户希望了解其用法和参数说明。 根据用户提供的引用,这是一个标准的SQLite查询接口,通常用于ContentProvider或直接操作SQLiteDatabase。 注意:Cursor本身是一个接口,实际返回的是具体实现类(如SQLiteCursor)。query方法通常由SQLiteDatabase或ContentProvider调用。 参数说明(参考Android文档): public Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) 下面详细说明每个参数: 1. distinct: 是否消除重复行,true表示只返回不同的行。 2. table: 要查询的表名。 3. columns: 要返回的列名数组,如果为null则返回所有列(但不建议,因为效率低且可能泄露不必要的数据)。 4. selection: 查询条件子句(即WHERE子句的内容,不包括WHERE关键字),例如:"age > ? AND name = ?"。 5. selectionArgs: 查询条件中占位符(?)的具体,按顺序填充。 6. groupBy: 分组列,相当于SQL的GROUP BY子句(不包括GROUP BY关键字),例如:"department"。 7. having: 分组条件,相当于SQL的HAVING子句(不包括HAVING关键字),例如:"COUNT(*) > 5"。 8. orderBy: 排序方式,相当于SQL的ORDER BY子句(不包括ORDER BY关键字),例如:"age DESC, name ASC"。 9. limit: 限制返回的行数,可以包偏移量,例如:"10" 或 "5,10"(表示从第5行开始取10行)。 注意:这个方法返回一个Cursor对象,通过它可以遍历查询结果。 使用示例: 假设我们有一个员工表(employee),包以下列:_id(主键), name, age, department, salary。 我们想查询销售部门(department='sales')中年龄大于30岁的员工,按薪水降序排列,并且只取前10条记录。 代码如下: ```java SQLiteDatabase db = ...; // 获取数据库实例 String table = "employee"; String[] columns = {"_id", "name", "age", "salary"}; // 只返回这些列 String selection = "department=? AND age>?"; String[] selectionArgs = {"sales", "30"}; String orderBy = "salary DESC"; String limit = "10"; Cursor cursor = db.query(false, table, columns, selection, selectionArgs, null, // groupBy null, // having orderBy, limit); // 遍历Cursor if (cursor != null && cursor.moveToFirst()) { do { long id = cursor.getLong(cursor.getColumnIndex("_id")); String name = cursor.getString(cursor.getColumnIndex("name")); int age = cursor.getInt(cursor.getColumnIndex("age")); double salary = cursor.getDouble(cursor.getColumnIndex("salary")); // 处理数据... } while (cursor.moveToNext()); } // 最后记得关闭Cursor if (cursor != null) { cursor.close(); } ``` 注意事项: 1. 使用占位符(?)可以避免SQL注入,不要直接拼接字符串。 2. 查询结束后必须关闭Cursor,否则会造成资源泄露。 3. 在Android中,建议在非UI线程执行数据库操作。 另外,在ContentProvider中,我们通常使用ContentResolver.query(),它最终会调用ContentProvider的query方法,而在ContentProvider内部,我们通常会使用SQLiteDatabase的query方法。 引用说明:用户提供的引用是Android官方文档中SQLiteDatabase的query方法签名[^1]。</think>### Cursor query 方法详解 在 Android 开发中,`Cursor query()` 方法用于执行数据库查询操作,返回包查询结果的 `Cursor` 对象。以下是各参数的详细说明和使用方法: #### 方法签名 ```java public Cursor query ( boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit ) ``` #### 参数说明 | 参数 | 类型 | 说明 | 示例 | |------|------|------|------| | `distinct` | `boolean` | 是否消除重复行 | `true` 返回唯一行 | | `table` | `String` | 要查询的表名 | `"employees"` | | `columns` | `String[]` | 要返回的列名数组 | `new String[]{"name", "age"}` | | `selection` | `String` | WHERE 条件子句 | `"department = ?"` | | `selectionArgs` | `String[]` | WHERE 条件参数 | `new String[]{"HR"}` | | `groupBy` | `String` | GROUP BY 分组条件 | `"department"` | | `having` | `String` | HAVING 过滤条件 | `"COUNT(*) > 5"` | | `orderBy` | `String` | ORDER BY 排序条件 | `"salary DESC"` | | `limit` | `String` | 返回行数限制 | `"10"` 或 `"5,10"` | #### 使用示例 ```java // 获取数据库实例 SQLiteDatabase db = dbHelper.getReadableDatabase(); // 执行查询 Cursor cursor = db.query( true, // distinct: 消除重复 "employees", // table: 员工表 new String[]{"name", "department", "salary"}, // columns: 查询列 "salary > ? AND department = ?", // selection: 条件 new String[]{"5000", "Engineering"}, // selectionArgs: 参数 "department", // groupBy: 按部门分组 "AVG(salary) > 6000", // having: 平均工资>6000 "salary DESC", // orderBy: 工资降序 "10" // limit: 返回10条 ); // 遍历结果 if (cursor != null && cursor.moveToFirst()) { do { String name = cursor.getString(cursor.getColumnIndex("name")); String dept = cursor.getString(cursor.getColumnIndex("department")); double salary = cursor.getDouble(cursor.getColumnIndex("salary")); Log.d("DB", name + " - " + dept + " - $" + salary); } while (cursor.moveToNext()); } // 关闭Cursor释放资源 if (cursor != null) { cursor.close(); } ``` #### 关键注意事项 1. **SQL 注入防护** - 始终使用 `selectionArgs` 传递参数 - 错误做法:`"name = '" + userName + "'"` - 正确做法:`"name = ?", new String[]{userName}` 2. **性能优化** - 只查询需要的列:避免使用 `null` 获取所有列 - 使用 `LIMIT` 限制数据量 - 复杂查询考虑建立索引 3. **资源管理** - 使用后必须调用 `cursor.close()` - 在 UI 线程避免耗时查询 - 使用 `Loader` 或 `CursorLoader` 异步加载 4. **空处理** ```java // 检查Cursor是否为空 if (cursor == null || cursor.getCount() == 0) { // 处理空结果 } ``` #### 与 ContentProvider 集成 在 `ContentProvider` 中实现查询: ```java @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = dbHelper.getReadableDatabase(); // 添加自定义查询逻辑 String groupBy = null; String having = null; String limit = uri.getQueryParameter("limit"); return db.query( false, // distinct "books", // table projection, // columns selection, // where selectionArgs, // where args groupBy, // group by having, // having sortOrder, // order by limit // limit ); } ``` #### 最佳实践 1. **使用 try-with-resources (API 19+)** ```java try (Cursor cursor = db.query(...)) { // 处理结果 } // 自动关闭Cursor ``` 2. **列索引缓存** ```java int nameIndex = cursor.getColumnIndex("name"); int ageIndex = cursor.getColumnIndex("age"); while (cursor.moveToNext()) { String name = cursor.getString(nameIndex); int age = cursor.getInt(ageIndex); } ``` 3. **批量处理大数据** ```java final int BATCH_SIZE = 50; do { // 处理当前批次 } while (cursor.moveToNext() && cursor.getPosition() % BATCH_SIZE != 0); ``` > **性能提示**:当查询结果超过 1MB 时,Android 系统会记录警告日志 `CursorWindow: Window is full`[^1],此时应考虑分页查询或优化数据量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值