Android IN子句和占位符

Android IN子句和占位符,android,sqlite,Android,Sqlite,我正在尝试在Android中执行以下SQL查询: String names=“'name1'、'name2”//在代码中,这是动态生成的 String query=“从名称所在的表中选择*”; Cursor Cursor=mDb.rawQuery(查询,新字符串[]{names}); 然而,Android并没有用正确的值替换问号。我可以执行以下操作,但这并不能防止SQL注入: String query=“从名称位于(“+names+”)中的表中选择*”; Cursor Cursor=mDb.r

我正在尝试在Android中执行以下SQL查询:

String names=“'name1'、'name2”//在代码中,这是动态生成的
String query=“从名称所在的表中选择*”;
Cursor Cursor=mDb.rawQuery(查询,新字符串[]{names});
然而,Android并没有用正确的值替换问号。我可以执行以下操作,但这并不能防止SQL注入:

String query=“从名称位于(“+names+”)中的表中选择*”;
Cursor Cursor=mDb.rawQuery(查询,空);

我怎样才能避开这个问题并能够使用IN子句?

遗憾的是,没有办法做到这一点(显然,
'name1'、'name2'
不是一个单一的值,因此不能在准备好的语句中使用)


因此,您必须降低您的视野(例如,通过创建非常特定的、不可重用的查询,如
WHERE name IN(?,?)
)或不使用存储过程,并尝试使用其他一些技术防止SQL注入

格式为“?,?,…,?”的字符串可以是动态创建的字符串,并安全地放入原始SQL查询中(因为它是不包含外部数据的受限表单),然后占位符可以正常使用

考虑一个函数
String makePlaceholders(int len)
,它返回用逗号分隔的
len
问号,然后:

String[]names={“name1”,“name2”};//先做需要做的事
String query=“从表中选择*”
+“其中名称位于(“+makePlaceholders(names.length)+”)”;
Cursor=mDb.rawQuery(查询,名称);
只需确保传递的值与传递的位置完全相同。SQLite中的默认最大值为999-至少在正常构建中是这样,但不确定Android:)

快乐编码


以下是一个实现:

字符串生成占位符(int len){
if(len<1){
//无论如何,这将导致无效查询。。
抛出新的RuntimeException(“无占位符”);
}否则{
StringBuilder sb=新StringBuilder(len*2-1);
某人加上(“?”);
对于(int i=1;i
实际上,您可以使用android的原生查询方式,而不是rawQuery:

public int updateContactsByServerIds(ArrayList<Integer> serverIds, final long groupId) {
    final int serverIdsCount = serverIds.size()-1; // 0 for one and only id, -1 if empty list
    final StringBuilder ids = new StringBuilder("");
    if (serverIdsCount>0) // ambiguous "if" but -1 leads to endless cycle
        for (int i = 0; i < serverIdsCount; i++)
            ids.append(String.valueOf(serverIds.get(i))).append(",");
    // add last (or one and only) id without comma
    ids.append(String.valueOf(serverIds.get(serverIdsCount))); //-1 throws exception
    // remove last comma
    Log.i(this,"whereIdsList: "+ids);
    final String whereClause = Tables.Contacts.USER_ID + " IN ("+ids+")";

    final ContentValues args = new ContentValues();
    args.put(Tables.Contacts.GROUP_ID, groupId);

    int numberOfRowsAffected = 0;
    SQLiteDatabase db = dbAdapter.getWritableDatabase());
        try {
            numberOfRowsAffected = db.update(Tables.Contacts.TABLE_NAME, args, whereClause, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        dbAdapter.closeWritableDB();


    Log.d(TAG, "updateContactsByServerIds() numberOfRowsAffected: " + numberOfRowsAffected);

    return numberOfRowsAffected;
}
public int updateContactsByServerId(ArrayList ServerId,最后一个长groupId){
final int serveridscont=serverIds.size()-1;//0表示一个且唯一的id,如果列表为空,则为-1
最终StringBuilder ID=新StringBuilder(“”);
if(serverIdsCount>0)//不明确的“if”但是-1会导致无休止的循环
对于(int i=0;i
您可以使用
TextUtils.join(“,”,parameters)
来利用sqlite绑定参数,其中
parameters
是一个带有
“?”
占位符的列表,结果字符串类似于
“?,?,…,”

下面是一个小例子:

Set<Integer> positionsSet = membersListCursorAdapter.getCurrentCheckedPosition();
List<String> ids = new ArrayList<>();
List<String> parameters = new ArrayList<>();
for (Integer position : positionsSet) {
    ids.add(String.valueOf(membersListCursorAdapter.getItemId(position)));
    parameters.add("?");
}
getActivity().getContentResolver().delete(
    SharedUserTable.CONTENT_URI,
    SharedUserTable._ID + " in (" + TextUtils.join(",", parameters) + ")",
    ids.toArray(new String[ids.size()])
);
设置位置Set=membersListCursorAdapter.getCurrentCheckedPosition();
列表ID=新的ArrayList();
列表参数=新的ArrayList();
对于(整数位置:位置设置){
add(String.valueOf(membersListCursorAdapter.getItemId(position));
参数。添加(“?”);
}
getActivity().getContentResolver().delete(
SharedUserTable.CONTENT\u URI,
“+TextUtils.join”(“,”,parameters)+”)中的SharedUserTable._ID+”,
ids.toArray(新字符串[ids.size()])
);

简短示例,基于用户166390的回答:

public Cursor selectRowsByCodes(String[] codes) {
    try {
        SQLiteDatabase db = getReadableDatabase();
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

        String[] sqlSelect = {COLUMN_NAME_ID, COLUMN_NAME_CODE, COLUMN_NAME_NAME, COLUMN_NAME_PURPOSE, COLUMN_NAME_STATUS};
        String sqlTables = "Enumbers";

        qb.setTables(sqlTables);

        Cursor c = qb.query(db, sqlSelect, COLUMN_NAME_CODE+" IN (" +
                        TextUtils.join(",", Collections.nCopies(codes.length, "?")) +
                        ")", codes,
                null, null, null); 
        c.moveToFirst();
        return c;
    } catch (Exception e) {
        Log.e(this.getClass().getCanonicalName(), e.getMessage() + e.getStackTrace().toString());
    }
    return null;
}

如接受答案中所示,但不使用自定义函数生成逗号分隔的“?”。请检查下面的代码

String[] names = { "name1", "name2" }; // do whatever is needed first
String query = "SELECT * FROM table"
    + " WHERE name IN (" + TextUtils.join(",", Collections.nCopies(names.length, "?"))  + ")";
Cursor cursor = mDb.rawQuery(query, names);

这是无效的

String subQuery = "SELECT _id FROM tnl_partofspeech where part_of_speech = 'noun'";
Cursor cursor = SQLDataBase.rawQuery(
                "SELECT * FROM table_main where part_of_speech_id IN (" +
                        "?" +
                        ")",
                new String[]{subQuery}););
String subQuery = "SELECT _id FROM tbl_partofspeech where part_of_speech = 'noun'";
Cursor cursor = SQLDataBase.rawQuery(
                "SELECT * FROM table_main where part_of_speech_id IN (" +
                        subQuery +
                        ")",
                null);
这是有效的

String subQuery = "SELECT _id FROM tnl_partofspeech where part_of_speech = 'noun'";
Cursor cursor = SQLDataBase.rawQuery(
                "SELECT * FROM table_main where part_of_speech_id IN (" +
                        "?" +
                        ")",
                new String[]{subQuery}););
String subQuery = "SELECT _id FROM tbl_partofspeech where part_of_speech = 'noun'";
Cursor cursor = SQLDataBase.rawQuery(
                "SELECT * FROM table_main where part_of_speech_id IN (" +
                        subQuery +
                        ")",
                null);
使用ContentResolver

String subQuery = "SELECT _id FROM tbl_partofspeech where part_of_speech = 'noun' ";

final String[] selectionArgs = new String[]{"1","2"};
final String selection = "_id IN ( ?,? )) AND part_of_speech_id IN (( " + subQuery + ") ";
SQLiteDatabase SQLDataBase = DataBaseManage.getReadableDatabase(this);

SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables("tableName");

Cursor cursor =  queryBuilder.query(SQLDataBase, null, selection, selectionArgs, null,
        null, null);
在科特林,您可以使用


我使用
API来实现以下目的:

final String[] args = Stream.of("some","data","for","args").toArray(String[]::new);
final String placeholders = Stream.generate(() -> "?").limit(args.length).collect(Collectors.joining(","));
final String selection = String.format("SELECT * FROM table WHERE name IN(%s)", placeholders);

db.rawQuery(selection, args);

是的,这是在SQLite和几乎任何其他SQL数据库中使用参数化IN()查询的(唯一)方法。使用此方法,我扩充了我使用的ContentProvider,并在query()方法中添加了逻辑以测试是否存在:“IN?”,如果找到,是否对原始选择中出现的“?”进行计数,与传递的参数的长度相比,为差异组装一个“?,?,…?”,并用生成的问号集合替换原始的“IN?”。这使得逻辑几乎可以在全球范围内使用,对于我来说,它似乎工作得很好。我确实需要添加一些特殊的设置来过滤空的IN列表,在这种情况下,“IN?”现在被替换为“1”。当然,最愚蠢的是,如果你要用N个问号来创建自己的字符串,你最好直接对数据进行编码。假设已清理。整个
makeplaceholder
可以替换为
TextUtils.join(“,”,Collections.nCopies(len,“?”)
。不太详细。
原因:android.database.sqlite.SQLiteException:near“,”:语法错误(代码1):,编译时:从任务中选择url,其中url=?,?,?,?,?,?,?