Room 数据库 RawQuery() 不适用于 "IN" 和 "NOT IN" 子句
Room database RawQuery() is not work on "IN" and "NOT IN" clause
我有一个 table 像 UserTable。
@Entity
public class UserTable{
@PrimaryKey(autoGenerate = true)
private int userId;
private String userName;
private String userEmailId;
// Below code is getter and setter of this class.
}
@Dao
public interface UserDao {
@Query("SELECT * FROM userTable")
public List<UserTable> loadAllUsers();
@Insert
public long insertUserTable(UserTable userTable);
@Insert
public long[] insertUserTables(UserTable... userTables);
@Update
public int updateUserTable(UserTable userTable);
@Delete
public int deleteUserTable(UserTable userTable);
@RawQuery
public abstract List<UserTable> loadAllUserListByGivenIds
(SupportSQLiteQuery query);
public default List<UserTable> loadAllUserListByIds(long[] userIds) {
List<UserTable> list;
ArrayList<Object> argsList = new ArrayList<>();
String selectQuery = "SELECT * FROM UserTable WHERE userId IN (?);";
argsList.add(userIds);
SimpleSQLiteQuery simpleSQLiteQuery = new SimpleSQLiteQuery(selectQuery, argsList.toArray());
list = loadAllUserListByGivenIds(simpleSQLiteQuery);
return list;
}
}
// 现在在我的 MainActivity.class 文件中,我使用了以下代码:
List<UserTable> userList= databaseClient
.getAppDatabase()
.userDao()
.loadAllUserListByIds(new long[]{1L,2L});
我的查询在普通数据库中是 运行,但是当我传递用户 ID 数组时,在 dao 的 @RawQuery() 方法中 class 不支持使用的“IN”子句在 where 条件下“WHERE userId IN (?)”。
如何,我将在房间数据库的@RawQuery() 中使用“IN”子句。
使用起来更容易 @Query
就这么简单:-
@Query("SELECT * FROM UserTable WHERE userId IN (:idList)")
public List<UserTable> getWhatever(long[] idList);
然后您将使用 getWhatever(new long[]{1L,2L})
如果你需要它一个@rawQuery,尽管你可以这样做(为方便起见,使用以前的答案代码):-
private List<TableXEntity> loadAllUserListByIds(int order,long[] idList) {
StringBuilder idListAsCSV = new StringBuilder(); //<<<<<<<<<<
boolean afterFirst = false; //<<<<<<<<<<
//<<<<<<<<<< all of the loop to create the CSV
for (Long l: idList) {
if (afterFirst) {
idListAsCSV.append(",");
}
afterFirst = true;
idListAsCSV.append(String.valueOf(l));
}
StringBuilder sb = new StringBuilder("SELECT * FROM ").append(DBHelper.TableX.NAME);
sb.append(" WHERE " + DBHelper.TableX.COLUMN_ID + " IN(").append(idListAsCSV).append(") "); //<<<<<<<<<<
switch (order) {
case DBHelper.TableX.FIRSTNAME_DESCENDING:
sb.append(DBHelper.TableX.ORDER_BY_FIRSTNAME_DESC);
break;
case DBHelper.TableX.FIRSTNAME_ASCENDING:
sb.append(DBHelper.TableX.ORDER_BY_FIRSTNAME_ASC);
break;
case DBHelper.TableX.LASTNAME_DESCENDING:
sb.append(DBHelper.TableX.ORDER_BY_LASTNAME_DESC);
break;
case DBHelper.TableX.LASTNAME_ASCENDING:
sb.append(DBHelper.TableX.ORDER_BY_LASTNAME_ASC);
break;
default:
break;
}
sb.append(";");
return roomDao.rawq(new SimpleSQLiteQuery(sb.toString(),null));
}
即提供 CSV (虽然我依稀记得能够传递一个数组)
要使用绑定参数(绑定参数的推荐方式可以防止 SQL 注入),那么您需要一个 ?对于每个值和相应的对象数组。
所以对于 3 个 id,您需要 IN(?,?,?) 和实际值,即 Object[] 中的绑定参数。以下是执行此操作的示例,注意它显示了构建 Object[](绑定 arguments/values)的 2 种方法:-
private List<TableXEntity> loadByidList(long[] idlist) {
List<Object> bindargs = new ArrayList<>(); // way 1
Object[] args4Bind = new Object[idlist.length]; // way 2
StringBuilder placeholders = new StringBuilder(); // for the ? placeholders
/* Build the sql before the place holders */
StringBuilder sql = new StringBuilder("SELECT * FROM ")
.append(DBHelper.TableX.NAME)
.append(" WHERE ")
.append(DBHelper.TableX.COLUMN_ID)
.append(" IN (");
boolean afterfirst = false;
int i = 0; /* using for each so have index counter (as opposed to for(int i=0 ....) */
for (long l: idlist) {
bindargs.add(l); // for way 1
args4Bind[i++] = String.valueOf(l); // for way 2
if (afterfirst) {
placeholders.append(",");
}
afterfirst = true;
placeholders.append("?");
}
/* finalise the SQL */
sql.append(placeholders.toString())
.append(");");
//return roomDao.rawq(new SimpleSQLiteQuery(sql.toString(),bindargs.toArray())); // way 1
return roomDao.rawq(new SimpleSQLiteQuery(sql.toString(),args4Bind)); // way 2
}
请试试这个,它已经起作用了!
试试这个简单的技巧来传递 IN 运算符的参数-
List<Object> argList = new ArrayList<>();
argList.add("3");
argList.add("6");
然后准备原始查询字符串:
注意 - 将参数列表大小与“?”匹配尺码
String selectQuery = "SELECT * FROM task WHERE id IN (?,?)";
在此之后将原始查询字符串传递给 SimpleSQLiteQuery-
SimpleSQLiteQuery rawQuery = new SimpleSQLiteQuery(selectQuery, args.toArray());
然后使用 DAO 获取列表:
List<UserTable> taskList1=DatabaseClient
.getInstance(getApplicationContext())
.getAppDatabase()
.userTableDAO()
.getAllList(query);
我们可以用更简单的方式在kotlin中实现。
让我们创建两个辅助方法
object Helper {
fun sqlIn(list: List<Any>, bindArgs: MutableList<Any>): String {
bindArgs.apply { this.addAll(list) }
return "IN (${list.joinToString(",") { "?" }})"
}
fun sqlNotIn(list: List<Any>, bindArgs: MutableList<Any>): String = "NOT ${sqlIn(list, bindArgs)}"
}
然后你可以在其他任何地方使用它
val ids = listOf(1, 2, 3)
val ownerId = 10
val bindArgs = mutableListOf<Any>()
val query = "SELECT * FROM posts WHERE id ${Helper.sqlIn(ids, bindArgs)} AND owner_id = ?"
bindArgs.add(ownerId)
dao.query(
SimpleSQLiteQuery(query, bindArgs.toTypedArray())
)
我有一个 table 像 UserTable。
@Entity
public class UserTable{
@PrimaryKey(autoGenerate = true)
private int userId;
private String userName;
private String userEmailId;
// Below code is getter and setter of this class.
}
@Dao
public interface UserDao {
@Query("SELECT * FROM userTable")
public List<UserTable> loadAllUsers();
@Insert
public long insertUserTable(UserTable userTable);
@Insert
public long[] insertUserTables(UserTable... userTables);
@Update
public int updateUserTable(UserTable userTable);
@Delete
public int deleteUserTable(UserTable userTable);
@RawQuery
public abstract List<UserTable> loadAllUserListByGivenIds
(SupportSQLiteQuery query);
public default List<UserTable> loadAllUserListByIds(long[] userIds) {
List<UserTable> list;
ArrayList<Object> argsList = new ArrayList<>();
String selectQuery = "SELECT * FROM UserTable WHERE userId IN (?);";
argsList.add(userIds);
SimpleSQLiteQuery simpleSQLiteQuery = new SimpleSQLiteQuery(selectQuery, argsList.toArray());
list = loadAllUserListByGivenIds(simpleSQLiteQuery);
return list;
}
}
// 现在在我的 MainActivity.class 文件中,我使用了以下代码:
List<UserTable> userList= databaseClient
.getAppDatabase()
.userDao()
.loadAllUserListByIds(new long[]{1L,2L});
我的查询在普通数据库中是 运行,但是当我传递用户 ID 数组时,在 dao 的 @RawQuery() 方法中 class 不支持使用的“IN”子句在 where 条件下“WHERE userId IN (?)”。
如何,我将在房间数据库的@RawQuery() 中使用“IN”子句。
使用起来更容易 @Query
就这么简单:-
@Query("SELECT * FROM UserTable WHERE userId IN (:idList)")
public List<UserTable> getWhatever(long[] idList);
然后您将使用 getWhatever(new long[]{1L,2L})
如果你需要它一个@rawQuery,尽管你可以这样做(为方便起见,使用以前的答案代码):-
private List<TableXEntity> loadAllUserListByIds(int order,long[] idList) {
StringBuilder idListAsCSV = new StringBuilder(); //<<<<<<<<<<
boolean afterFirst = false; //<<<<<<<<<<
//<<<<<<<<<< all of the loop to create the CSV
for (Long l: idList) {
if (afterFirst) {
idListAsCSV.append(",");
}
afterFirst = true;
idListAsCSV.append(String.valueOf(l));
}
StringBuilder sb = new StringBuilder("SELECT * FROM ").append(DBHelper.TableX.NAME);
sb.append(" WHERE " + DBHelper.TableX.COLUMN_ID + " IN(").append(idListAsCSV).append(") "); //<<<<<<<<<<
switch (order) {
case DBHelper.TableX.FIRSTNAME_DESCENDING:
sb.append(DBHelper.TableX.ORDER_BY_FIRSTNAME_DESC);
break;
case DBHelper.TableX.FIRSTNAME_ASCENDING:
sb.append(DBHelper.TableX.ORDER_BY_FIRSTNAME_ASC);
break;
case DBHelper.TableX.LASTNAME_DESCENDING:
sb.append(DBHelper.TableX.ORDER_BY_LASTNAME_DESC);
break;
case DBHelper.TableX.LASTNAME_ASCENDING:
sb.append(DBHelper.TableX.ORDER_BY_LASTNAME_ASC);
break;
default:
break;
}
sb.append(";");
return roomDao.rawq(new SimpleSQLiteQuery(sb.toString(),null));
}
即提供 CSV (虽然我依稀记得能够传递一个数组)
要使用绑定参数(绑定参数的推荐方式可以防止 SQL 注入),那么您需要一个 ?对于每个值和相应的对象数组。
所以对于 3 个 id,您需要 IN(?,?,?) 和实际值,即 Object[] 中的绑定参数。以下是执行此操作的示例,注意它显示了构建 Object[](绑定 arguments/values)的 2 种方法:-
private List<TableXEntity> loadByidList(long[] idlist) {
List<Object> bindargs = new ArrayList<>(); // way 1
Object[] args4Bind = new Object[idlist.length]; // way 2
StringBuilder placeholders = new StringBuilder(); // for the ? placeholders
/* Build the sql before the place holders */
StringBuilder sql = new StringBuilder("SELECT * FROM ")
.append(DBHelper.TableX.NAME)
.append(" WHERE ")
.append(DBHelper.TableX.COLUMN_ID)
.append(" IN (");
boolean afterfirst = false;
int i = 0; /* using for each so have index counter (as opposed to for(int i=0 ....) */
for (long l: idlist) {
bindargs.add(l); // for way 1
args4Bind[i++] = String.valueOf(l); // for way 2
if (afterfirst) {
placeholders.append(",");
}
afterfirst = true;
placeholders.append("?");
}
/* finalise the SQL */
sql.append(placeholders.toString())
.append(");");
//return roomDao.rawq(new SimpleSQLiteQuery(sql.toString(),bindargs.toArray())); // way 1
return roomDao.rawq(new SimpleSQLiteQuery(sql.toString(),args4Bind)); // way 2
}
请试试这个,它已经起作用了!
试试这个简单的技巧来传递 IN 运算符的参数-
List<Object> argList = new ArrayList<>();
argList.add("3");
argList.add("6");
然后准备原始查询字符串:
注意 - 将参数列表大小与“?”匹配尺码
String selectQuery = "SELECT * FROM task WHERE id IN (?,?)";
在此之后将原始查询字符串传递给 SimpleSQLiteQuery-
SimpleSQLiteQuery rawQuery = new SimpleSQLiteQuery(selectQuery, args.toArray());
然后使用 DAO 获取列表:
List<UserTable> taskList1=DatabaseClient
.getInstance(getApplicationContext())
.getAppDatabase()
.userTableDAO()
.getAllList(query);
我们可以用更简单的方式在kotlin中实现。
让我们创建两个辅助方法
object Helper { fun sqlIn(list: List<Any>, bindArgs: MutableList<Any>): String { bindArgs.apply { this.addAll(list) } return "IN (${list.joinToString(",") { "?" }})" } fun sqlNotIn(list: List<Any>, bindArgs: MutableList<Any>): String = "NOT ${sqlIn(list, bindArgs)}" }
然后你可以在其他任何地方使用它
val ids = listOf(1, 2, 3) val ownerId = 10 val bindArgs = mutableListOf<Any>() val query = "SELECT * FROM posts WHERE id ${Helper.sqlIn(ids, bindArgs)} AND owner_id = ?" bindArgs.add(ownerId) dao.query( SimpleSQLiteQuery(query, bindArgs.toTypedArray()) )