非西语区域设置下SQLiteDatabase整数查询崩溃问题解决咨询
问题根源
你遇到的这个崩溃完全是MessageFormat的区域特性搞的鬼:在阿拉伯语这类非西语区域设备上,它会把数字1自动转换成当地的阿拉伯-Indic数字١。而SQLite根本不认识这种本地化数字,它会把١当成一个列名来解析,自然就抛出"no such column"的错误了。
解决方案
下面给你几个靠谱的解决办法,按推荐程度排序:
1. 用SQLite参数化查询(强烈推荐)
这是最安全也最规范的做法,不仅能彻底解决区域本地化问题,还能防止SQL注入攻击。把你的代码改成用?作为占位符,而不是自己拼接SQL:
// 把查询语句改成用?作为参数占位符 private static final String JUMP_COUNT_QUERY = "select count(*) " + "from log_entry " + "inner join log_entry_rig on log_entry._id = log_entry_rig.log_entry " + "where log_entry_rig.rig = ?"; // 执行查询时把参数传给rawQuery的第二个参数 Cursor cursor = null; try { cursor = db.rawQuery(JUMP_COUNT_QUERY, new String[]{String.valueOf(rigId)}); // 后续逻辑... } finally { // 别忘了关闭cursor if (cursor != null) { cursor.close(); } }
这样SQLite会自己处理参数的格式化,完全不受系统区域设置影响,而且从根本上避免了SQL注入的风险,这是Android操作SQLite的标准做法。
2. 指定MessageFormat使用中立区域
如果你因为某些原因必须继续使用MessageFormat,那可以强制它使用Locale.ROOT或者Locale.US来格式化,这样数字就会保持ASCII的0-9格式:
// 创建MessageFormat时指定中立区域 MessageFormat format = new MessageFormat(JUMP_COUNT_QUERY_FORMAT, Locale.ROOT); String query = format.format(new Object[]{rigId}); Cursor cursor = null; try { cursor = db.rawQuery(query, null); // 后续逻辑... } finally { if (cursor != null) { cursor.close(); } }
不过这种方法还是存在SQL注入的风险,所以只推荐在没办法用参数化查询的场景下临时用。
3. 手动把本地化数字转成ASCII数字
还有一种笨办法,就是把rigId转换成字符串后,替换掉所有非ASCII的数字字符。比如写一个工具方法:
private static String convertToAsciiNumber(String localizedNumber) { StringBuilder sb = new StringBuilder(); for (char c : localizedNumber.toCharArray()) { if (Character.isDigit(c)) { // 把本地化数字转成ASCII数字 sb.append(Character.getNumericValue(c)); } else { sb.append(c); } } return sb.toString(); } // 使用时 String query = MessageFormat.format(JUMP_COUNT_QUERY_FORMAT, convertToAsciiNumber(String.valueOf(rigId)));
这种方法比较繁琐,而且容易遗漏某些特殊区域的数字格式,所以不推荐作为首选方案。
总结
优先选择参数化查询,这是行业标准的最佳实践,既能解决当前的崩溃问题,又能提升代码的安全性。如果实在要用字符串拼接的方式,再考虑指定MessageFormat的区域。
内容的提问来源于stack exchange,提问作者PIXP
相关产品推荐
相关产品推荐

