问题
是否可以在运行时构造查询?
用例
@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);
stdName1
和 stdId1
是列名
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类。
根据文档