如何在运行时动态查询房间数据库



问题

是否可以在运行时构造查询?


用例

@Query("SELECT * FROM playlist " +
        "WHERE playlist_title LIKE '% :playlistTitle %' " +
        "GROUP BY playlist_title " +
        "ORDER BY playlist_title " +
        "LIMIT :limit")
 List<IPlaylist> searchPlaylists(String playlistTitle, int limit);

limit零件是可选的。也就是说,它应该能够在有或没有限制的情况下执行相同的查询。


更复杂的用例

在上一种情况下,可以每次都使用有或没有限制部分的两个静态查询。但是有时我们可能不得不处理更复杂的情况,例如构建过滤器。

在这种情况下,与上一个示例不同,将有多个可选零件。对于书籍表,我们可能需要根据书籍所属的类别进行过滤,作者名称,价格范围,出版日期等。几乎不可能使用这些零件的所有组合进行静态查询。

房间支持@RawQuery注释以在运行时构造查询。


步骤1:做DAO方法

@RawQuery注释而不是普通@Query标记DAO方法。

@Dao
interface BooksDao{
    @RawQuery
    List<Book> getBooks(SupportSQLiteQuery query);
}

步骤2:构造查询

房间使用准备好的语句进行安全性和编译时间验证。因此,在构造查询时,我们需要单独存储查询字符串并绑定参数。

在此示例中,我使用可查询字符串的变量queryString,而args用于绑定参数。

(请注意,我使用文本编辑器编写代码。因此,可能会有错字或简单的语法错误。如果您找到任何内容,请在评论中让我知道或编辑帖子。(

// Query string
String queryString = new String();
// List of bind parameters
List<Object> args = new ArrayList();
boolean containsCondition = false;
// Beginning of query string
queryString += "SELECT * FROM BOOKS";
// Optional parts are added to query string and to args upon here
if(!authorName.isEmpty()){
    queryString += " WHERE";
    queryString += " author_name LIKE ?%";
    args.add(authorName);
    containsCondition = true;
}
if(fromDate!=null){
    
    if (containsCondition) {
        queryString += " AND";
    } else {
        queryString += " WHERE";
        containsCondition = true;
    }
    queryString += " publication_date AFTER ?";
    args.add(fromDate.getTime());
}
if(toDate!=null){
    
    if (containsCondition) {
        queryString += " AND";
    } else {
        queryString += " WHERE";
        containsCondition = true;
    }
    queryString += " publication_date BEFORE ?";
    args.add(toDate.getTime());
}
// End of query string
queryString += ";";

步骤3:执行查询

SimpleSQLiteQuery query = new SimpleSQLiteQuery(queryString, args.toArray());
List<Book> result = booksDao.getBooks(query);



注释

  • 就像普通的Query一样,RawQuery支持返回的原始光标,实体,Pojos和Pojos,带有嵌入式字段
  • RawQuery支持关系

在我的经验中(简短(使用不可能的房间,而不是因为是房间的限制,而是因为@commonsware暗示地评论了一个限制在sqlite上。您需要两个查询,因此在您的dao中进行了两种方法。

我会有类似的东西:

@Query("SELECT * FROM playlist " +
    "WHERE playlist_title LIKE '% :playlistTitle %' " +
    "GROUP BY playlist_title " +
    "ORDER BY playlist_title " +
    "LIMIT :limit")
List<IPlaylist> searchPlaylists(String playlistTitle, int limit);
@Query("SELECT * FROM playlist " +
    "WHERE playlist_title LIKE '% :playlistTitle %' " +
    "GROUP BY playlist_title " +
    "ORDER BY playlist_title ")
List<IPlaylist> searchPlaylists(String playlistTitle);

然后在其他地方进行旁路:

if (limit.isPresent()) {
   return playlistDao.searchPlaylists(title, limit.get());
} else {
   return playlistDao.searchPlaylists(title);
}

这是我目前能想到的最好的选择。

而不是编写多个查询,而是将负值传递给限制子句。因为如果查询发生变化,我必须更新两个错误的查询。

官方doc-> 如果限制表达式评估为负值,则返回的行数上没有上限。您可以在此处找到https://sqlite.org/lang_select.html并读取限制条款部分。

所以我会这样做,

@Query("SELECT * FROM playlist " +
    "WHERE playlist_title LIKE '% :playlistTitle %' " +
    "GROUP BY playlist_title " +
    "ORDER BY playlist_title " +
    "LIMIT :limit")
List<IPlaylist> searchPlaylists(String playlistTitle, int limit);

,当您不想应用过滤器时通过负面。

return playlistDao.searchPlaylists(title, limit.isPresent() ? limit.get() : -1)

在我的情况下它正在工作。

更新[2018年12月21日]

如果您使用的是Kotlin使用默认值。

@JvmOverloads
@Query("SELECT * FROM playlist " +
        "WHERE playlist_title LIKE '% :playlistTitle %' " +
        "GROUP BY playlist_title " +
        "ORDER BY playlist_title " +
        "LIMIT :limit")
fun searchPlaylists(playlistTitle: String, limit: Int = -1): List<IPlaylist>

@JvmOverloads使其与Java兼容。它为Java生成了两种单独的方法。

使用supportsqlitequery。

https://developer.android.com/reference/android/anch/persistence/db/supportsqlitequery

最新版本1.1.1现在使用supportsqlitequery。

带有键入绑定的查询。最好使用此API而不是 RAWQUERY(String,String [](,因为它允许绑定类型安全 参数。

@Dao
     interface RawDao {
         @RawQuery(observedEntities = User.class)
         LiveData<List<User>> getUsers(SupportSQLiteQuery query);
     }

用法:

     LiveData<List<User>> liveUsers = rawDao.getUsers( new 
SimpleSQLiteQuery("SELECT * FROM User ORDER BY name DESC"));

将您的gradle更新为1.1.1

implementation 'android.arch.persistence.room:runtime:1.1.1'
implementation 'android.arch.lifecycle:extensions:1.1.1'
annotationProcessor "android.arch.persistence.room:compiler:1.1.1"

注意:如果您升级到1.1.1,并且正在使用字符串而不是supportsqlitequery,

您将获得错误:

RAWQUERY不再允许传递字符串。请用 android.ark.persistence.db.supportsqlitequery。

使用SupportSqliteQuery如上所述将解决问题。

注意:确保您传递SupportSqliteQuery查询参数,否则将获得此错误:

RAWQUERY方法应具有1个和1个参数,带有类型字符串 或支持Sqlitequery

房间中没有类似可选的参数,但是有一个@rawquery注释,您可以在其中以Query为字符串,以便您可以构建SQL在运行时查询。我认为这对您有用。

这是官方文档中的示例:

@Dao
 interface RawDao {
     @RawQuery
     User getUser(String query);
 }

这是您可以使用它的方法:

User user = rawDao.getUser("SELECT * FROM User WHERE id = 3 LIMIT 1");

重要: RAWQUERY方法必须返回非空隙类型

重要:这可以在1.1.0-Alpha3

中提供。我会向您展示使用两个变量使用子句的示例。这样做:

@Query("SELECT * FROM Student WHERE stdName1= :myname AND stdId1=:myid")
List<Student> fetchAllData(String myname,int myid);

stdName1stdId1是列名

for Kotlin-room-viewModel

@Query("SELECT * FROM schedule_info_table where schedule_month = :monthValue ORDER BY schedule_date_time ASC")
fun getThisMonthSchedules(monthValue: Int): Flow<List<SchedulesInformation>>
@Query("select * from task where state = :states and sentdate between :fromdate and :todate")
  List<Task> getFilterAll(String states, String fromdate, String todate);

在这里我们需要使用列名状态。每当需要实现自定义查询的需要时,只需通过活动或片段通过参数传递值,就会进入我们将在查询中应用的接口。就像上面代码中的示例(:fromdate:todate(

结肠是必须的。您将在查询中使用哪个参数,我们将在使用:符号开始之前提及。

@anderson k&amp;@Juanky Soriano,我同意@commonsware,

房间库有一些限制,然后我们也可以使用支持SQLITE数据库的@query()在房间数据库上编写一个完全动态的查询

String mQuery = "SELECT * FROM foobar WHERE columnName1 IN ('value_1','value_2') and columnName2 In('value_3','value_4')";
AppDatabase appDatabase = Room.databaseBuilder(getApplicationContext(),
        AppDatabase.class, "database-name").build();
Cursor mCursor = AppDatabase.getAppDatabase(context).getOpenHelper().getReadableDatabase().query(myQuery);

现在,您可以将光标的行数据转换为POJO类。

根据文档

最新更新