自定义 Android 房间 Where 子句

Customize Android Room Where Clause

我正在尝试根据 2 个微调器字段过滤对象列表。用户可以选择要过滤的字段,并非所有字段都必须有值(可以留空)。与其在我的 Dao 中编写大量查询,不如编写一个查询。

我的两个字段是颜色和类别。如果选择了特定颜色但未选择类别,那么我希望我的查询 return 所有具有该颜色的对象,但如果颜色和类别都已填充,那么我希望我的查询 return 那些特定的对象项,反之亦然。在 SQLite 中,您可以使用它来 return table 中所有黑色的对象,无论类别如何。

Select * from table where colour='black' and category = category

但是当我将我的变量发送到我的查询时,它会将类别放入引号中,因此房间正在寻找字符串“Category”而不是 return 所有类别。 (看起来像这样)

Select * from table where colour='black' and category = 'category'

最终我需要添加更多字段,这就是为什么我不想为每种可能性写出查询。

我考虑过使用 RawQuery,但我的实现不起作用。这是我在 Dao 中的界面

@interface RawDao
@RawQuery
List<Items> filterdItems(SupportSQLiteQuery query)

在我的 class

里面
query = "Select * from table where colour='black' and category = category";
List<items> items = RawDao.filteredItems(query);

但我一直收到错误“无法从静态方法引用非静态方法...”

有没有办法删除引号,这样我就可以 return 在微调器中没有选择任何对象的情况下所有对象?

RawDao 是一个接口,我们不能直接调用接口上的方法,请查看此 link 了解更多信息 How to call an interface method

您需要在 Database class 中创建 RawDao 的抽象实例,并且需要获取 Database class

的实例

例如:

@Database(entities = {DataClass.class}, version = dbversion in int, exportSchema = false)
public abstract class AppRoomDb extends RoomDatabase {
    private static String TAG = AppRoomDb.class.getSimpleName();
    private static volatile AppRoomDb INSTANCE;
    public abstract RawDao rawDao();

    public static AppRoomDb getInstance(final Context context) {
        if (INSTANCE == null) {
            synchronized (AppRoomDb.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            AppRoomDb.class, ROOM_DB_NAME)
                            .fallbackToDestructiveMigration()
                            .allowMainThreadQueries()
                            .build();
                }
            }
        }
        return INSTANCE;
    }
}

现在你可以获取RawDao接口的方法使用:

query = "Select * from table where colour='black' and category = category";
List<items> items = AppRoomDb.getInstance(context).rawDao().filteredItems(query);

有关 RoomDb 的更多信息,您可以查看此 link

您正在尝试通过接口名称 RawDao 访问 filteredItems。相反,您应该通过数据库实例调用它。使用数据库实例获取 DAO,然后使用所需的查询调用 filteredItems 方法:

query1 = "Select * from table where colour='black'";
query2 = "Select * from table where category = 'your_category'";
query3 = "Select * from table where colour='black' and category = 'your_category'";
SimpleSQLiteQuery query = new SimpleSQLiteQuery(query1) //Use query1 or query2, query3 
List<items> items = rawDao.filteredItems(query);

我相信下面的查询会做你想做的(至少在原则上):-

@Query("SELECT * FROM `table` WHERE color = (CASE WHEN length(:color) THEN :color ELSE color END) AND category =  (CASE WHEN length(:category) THEN :category ELSE category END)")
    List<Items> filteredItems(String color, String category);
  • 如果字段未 selected,则假定传递空字符串。

以下是一个基于您的代码的工作示例,用于演示上述内容。

假设颜色和类别是字符串。如果是 id,那么 CASE .... WHEN .... THEN .... END 会有点不同,但基本原理是相同的。也就是说,如果提供了 suitable 值,则使用传递的值,否则使用正在处理的列的值。

  • 如果颜色和类别是 id 的(长)那么你可以使用
    • @Query("SELECT * FROM table WHERE color = (CASE WHEN :color > 0 THEN :color ELSE color END) AND category = (CASE WHEN :category > 0 THEN :category ELSE category END)") List<Items> filteredItems(String color, String category);

    • 这假设 id 总是大于 0。

    • 此代码段尚未 tested/run 因此可能包含一些错误。

首先是实体 Table :-

@Entity
class Table {
    @PrimaryKey
    Long id = null;
    String color = null;
    String category = null;

    public Table(){}
    @Ignore
    public Table(String color, String category) {
        this.color = color;
        this.category = category;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }
}

RawDao接口:-

@Dao
interface RawDao {

    @Insert
    long insert(Table table);

    @Query("SELECT * FROM `table` WHERE color = (CASE WHEN length(:color) THEN :color ELSE color END) AND category =  (CASE WHEN length(:category) THEN :category ELSE category END)")
    List<Table> filteredItems(String color, String category);
}
  • 已使用 convenience/brevity 列表的注意事项(根据您查询名为 table 的 table 的查询,也许它应该是更好的项目)

@Database class TheDatabase :-

@Database(entities = {Table.class},version = 1)
abstract class TheDatabase extends RoomDatabase {
    abstract RawDao getDao();

    private static volatile TheDatabase instance = null;

    public static TheDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context,TheDatabase.class,"mydb")
                    .allowMainThreadQueries().build();
        }
        return instance;
    }
}

最后一个 activity 插入一些行然后提取数据,将其写入日志,使用以下排列:-

  1. 既不是颜色也不是类别(select全部)。即传递空字符串。
  2. 只有颜色值通过
  3. 颜色和类别值已通过
  4. 仅传递类别值。

:-

public class MainActivity extends AppCompatActivity {

    TheDatabase db;
    RawDao dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        db = TheDatabase.getInstance(this);
        dao = db.getDao();

        dao.insert(new Table("black","cat1"));
        dao.insert(new Table("green","cat1"));
        dao.insert(new Table("black","cat2"));
        dao.insert(new Table("green","cat2"));

        logTableInfo(dao.filteredItems("",""),"RUN1"); /* get everything as no values for either argument */
        logTableInfo(dao.filteredItems("black",""),"RUN2"); /*get color black irrespective of category */
        logTableInfo(dao.filteredItems("black","cat1"),"RUN3"); /* get color black in category cat1 */
        logTableInfo(dao.filteredItems("","cat1"),"RUN4"); /* get all that have cat1 as the category irresepctive of color */
    }

    private void logTableInfo(List<Table> items, String extra) {
        for(Table t: items) {
            Log.d("TABLEINFO" + extra,"Color is " + t.color + " Category is " + t.category + " ID is " + t.id);
        }
    }
}

结果

2021-07-05 21:53:52.616 D/TABLEINFORUN1: Color is black Category is cat1 ID is 1
2021-07-05 21:53:52.617 D/TABLEINFORUN1: Color is green Category is cat1 ID is 2
2021-07-05 21:53:52.617 D/TABLEINFORUN1: Color is black Category is cat2 ID is 3
2021-07-05 21:53:52.617 D/TABLEINFORUN1: Color is green Category is cat2 ID is 4


2021-07-05 21:53:52.619 D/TABLEINFORUN2: Color is black Category is cat1 ID is 1
2021-07-05 21:53:52.619 D/TABLEINFORUN2: Color is black Category is cat2 ID is 3


2021-07-05 21:53:52.619 D/TABLEINFORUN3: Color is black Category is cat1 ID is 1


2021-07-05 21:53:52.620 D/TABLEINFORUN4: Color is black Category is cat1 ID is 1
2021-07-05 21:53:52.620 D/TABLEINFORUN4: Color is green Category is cat1 ID is 2

额外

这是构建相对灵活的版本的示例,其中 SQL 是根据评论构建的。

通过灵活,它不特定于 table/entity,它可以满足任意数量的 column/value 对(但仅字符串值,BLOB (byte[]) 必须转换).使用 ContentValues 可以克服 BLOB 限制)。 但是,与使用@RawQuery 一样,您会丢失编译时检查,因此问题会导致运行时错误。

  • 请注意,这并不全面,而是一个可以满足某些情况的示例:-

首先是道:-

@RawQuery
List<Table> filteredItemsAlt(SupportSQLiteQuery query);

然后为方便起见,在 Table 实体中添加一个方法:-

public static SimpleSQLiteQuery buildQuery(String fromClause, String[] whereColumns, String[] whereValues) {

    // Run query that returns nothing if columns and args (values) mismatch
    SimpleSQLiteQuery rv = new SimpleSQLiteQuery(fromClause + " WHERE 1 = 2", null);
    if (whereColumns.length != whereValues.length) return rv;
    // OK to go so prepare build variables
    ArrayList<String> bindArgs = new ArrayList<>();
    StringBuilder whereclause = new StringBuilder();
    
    // For each column and therefore arg/value
    for(int i=0; i < whereColumns.length; i++) {
        if (whereclause.length() > 1) whereclause.append(" AND "); // if where clause is not empty then add AND
        whereclause.append(whereColumns[i]).append(" = "); // column name = part
        // if value/arg is null or string is empty then affectively ignore (could alternatively ignore the AND and column = column)
        if (whereValues[i] == null || whereValues[i].length() < 1) whereclause.append(whereColumns[i]);
        // otherwise prepare to bind the value i.e ? in the SQL and value stored for bind
        else {
            whereclause.append(" ? ");
            bindArgs.add(whereValues[i]);
        }
    }
    Log.d("WHERE_CLAUSE","WHERE clause is " + whereclause.toString());
    // check to se if there is a where clause, if so build the full SQL
    if (whereclause.length() > 0 ) {
        rv = new SimpleSQLiteQuery(fromClause + " WHERE " + whereclause.toString(), bindArgs.toArray());
    } else {
        // otherwise build sql without WHERE clause
        rv = new SimpleSQLiteQuery(fromClause);
    }
    return rv;
}
  • fromClause 是 SELECT SQL 包括 fromClause
  • whereColumns 是要比较的列列表
  • whereValues 是值列表(null 或空字符串以获取该列的所有行)

在 activity 中(复制第一个结果)你可以有 :-

    logTableInfo(dao.filteredItemsAlt(Table.buildQuery(
            "SELECT * FROM `table`",
            new String[]{"color","category"},
            new String[]{null,null}
    )),"ARUN1");
    logTableInfo(dao.filteredItemsAlt(Table.buildQuery(
            "SELECT * FROM `table`",
            new String[]{"color","category"},
            new String[]{"black",null}
    )),"ARUN2");
    logTableInfo(dao.filteredItemsAlt(Table.buildQuery(
            "SELECT * FROM `table`",
            new String[]{"color","category"},
            new String[]{"black","cat1"}
    )),"ARUN3");
    logTableInfo(dao.filteredItemsAlt(Table.buildQuery(
            "SELECT * FROM `table`",
            new String[]{"color","category"},
            new String[]{null,"cat1"}
    )),"ARUN4");

结果:-

2021-07-06 17:07:14.840 D/WHERE_CLAUSE: WHERE clause is color = color AND category = category
2021-07-06 17:07:14.841 D/TABLEINFOARUN1: Color is black Category is cat1 ID is 1
2021-07-06 17:07:14.841 D/TABLEINFOARUN1: Color is green Category is cat1 ID is 2
2021-07-06 17:07:14.841 D/TABLEINFOARUN1: Color is black Category is cat2 ID is 3
2021-07-06 17:07:14.841 D/TABLEINFOARUN1: Color is green Category is cat2 ID is 4
2021-07-06 17:07:14.841 D/WHERE_CLAUSE: WHERE clause is color =  ?  AND category = category
2021-07-06 17:07:14.843 D/TABLEINFOARUN2: Color is black Category is cat1 ID is 1
2021-07-06 17:07:14.843 D/TABLEINFOARUN2: Color is black Category is cat2 ID is 3
2021-07-06 17:07:14.843 D/WHERE_CLAUSE: WHERE clause is color =  ?  AND category =  ? 
2021-07-06 17:07:14.846 D/TABLEINFOARUN3: Color is black Category is cat1 ID is 1
2021-07-06 17:07:14.847 D/WHERE_CLAUSE: WHERE clause is color = color AND category =  ? 
2021-07-06 17:07:14.849 D/TABLEINFOARUN4: Color is black Category is cat1 ID is 1
2021-07-06 17:07:14.849 D/TABLEINFOARUN4: Color is green Category is cat1 ID is 2
  • 请注意,上面的内容是相对仓促地组合在一起的,因此可能需要进行一些调整,所以又是一个原则性的例子。 例如如果你想要订购怎么办? (修辞)