【问题标题】:Customize Android Room Where Clause自定义 Android Room Where 子句
【发布时间】:2023-03-22 09:06:01
【问题描述】:

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

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

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

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

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

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

我查看了使用 RawQuery,但我的实现没有奏效。这是我的 Dao 中的界面

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

在我的班级里

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

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

有没有办法删除引号,以便在微调器中未选择任何对象时返回所有对象?

【问题讨论】:

    标签: android sqlite android-room


    【解决方案1】:

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

    @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);
    
    • 如果未选择字段,则假定传递一个空字符串。

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

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

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

      • 这假设 id 总是大于 0。

      • 此 sn-p 尚未测试/运行,因此可能包含一些错误。

    首先是实体:-

    @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);
    }
    
    • 为了方便/简洁,使用了列表(根据您查询名为 table 的表的查询,也许它应该是更好的 item)

    @Database 类 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;
        }
    }
    

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

    1. 既不是颜色也不是类别(全选)。即传递了空字符串。
    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 并根据评论。

    通过灵活,它不特定于表/实体,它可以满足任意数量的列/值对(但只有字符串值,必须转换 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 是包含 fromClause 的 SELECT SQL
    • whereColumns 是要比较的列列表
    • whereValues 是值列表(null 或空字符串获取列的所有行)

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

        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
    
    • 请注意,以上内容的组合比较仓促,因此可能需要进行一些调整,因此更多的是原则上的示例。 例如如果你想订购它怎么办? (修辞)

    【讨论】:

    • 谢谢迈克!像魅力一样工作。有没有更好的方法来做到这一点?
    • @olivia.dayaram 替代方法是构建@RawQuery,而不是@Query,因此您可以构建SELECT * FROM table WHERE category = ? AND color = ?(值)或(未提供类别的地方作为排列之一)SELECT * FROM table WHERE category = category AND color = ? 即没有值然后硬编码列名而不是?。对于众多列,可以通过合适的方法构建 SQL,因此可以更简单。
    • 我构建了 RawQuery RawQuery List&lt;Item&gt; getItems(SupportSQLiteQuery query); ,这是我的查询代码 SimpleSQLiteQuery query = new SimpleSQLiteQuery("Select * from table where category = category") List&lt;item&gt; itemList = RawDao.getItems(query); 但它一直返回 null。如果我在我的表上运行相同的查询,我会得到结果。在文档中我看到`“SELECT * FROM Song WHERE id = ? LIMIT 1”, new Object[]{ songId}); ` 我需要在我的代码中使用“new Object..”吗?
    • @olivia.dayaram 如果您有任何要绑定的值(即 ? 的),那么您需要传递 Object[]。在根据 Additional 部分的示例中,您将看到 Object[] 如果 WHERE 子句不为空,即使 Object[] 可能为空(如两个类别都不为空)也没有提供颜色, A1RUN )。我应该发一条评论说参考 Additional 部分,因为它向您展示了一个工作示例。
    【解决方案2】:

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

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

    例如:

    @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

    【讨论】:

      【解决方案3】:

      您正在尝试通过接口名称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);
      

      【讨论】:

        猜你喜欢
        • 2021-05-27
        • 2014-03-18
        • 2013-10-03
        • 2014-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多