Как динамически запрашивать базу данных комнаты во время выполнения?
Эта проблема
Можно ли построить запрос во время выполнения?
Случай использования
@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
часть является необязательной. То есть он должен иметь возможность выполнять один и тот же запрос с ограничением или без него.
Более сложный вариант использования
В предыдущем случае можно сделать два статических запроса с лимитом и без него, и каждый раз можно использовать соответствующий запрос. Но иногда нам приходится сталкиваться с более сложными ситуациями, такими как создание фильтра.
В этом случае, в отличие от предыдущего примера, будет несколько дополнительных элементов. Для таблицы книг нам может потребоваться выполнить фильтрацию в соответствии с категорией, к которой принадлежит книга, именем автора, ценовым диапазоном, датой публикации и т.д. Почти невозможно выполнить статические запросы со всеми комбинациями этих частей.
Ответы
Ответ 1
В моем опыте (коротком) использовании Room это невозможно, а не из-за ограничения номера, но, как неявно комментирует @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);
}
Это лучший вариант, о котором я могу думать в данный момент.
Ответ 2
Вместо того, чтобы писать несколько запросов, я передаю отрицательное значение в предложение limit. Потому что, если есть изменение в запросе, я должен обновить оба запроса, который более подвержен ошибкам.
Официальный документ → Если выражение LIMIT имеет отрицательное значение, то нет верхней границы числа возвращаемых строк. вы можете найти его здесь https://sqlite.org/lang_select.html и прочитать раздел о предложении limit.
Так что я бы сделал что-то подобное,
@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)
Это работает в моем случае.
Обновлено [21 декабря 2018]
В случае, если вы используете 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.
Ответ 3
В комнате нет чего-то вроде необязательного параметра, но есть аннотация @RawQuery, где вы можете передать запрос как String, чтобы вы могли построить свой SQL-запрос во время выполнения. Думаю, это сработает для вас.
Вот пример из официальной документации:
@Dao
interface RawDao {
@RawQuery
User getUser(String query);
}
И вот как вы можете его использовать:
User user = rawDao.getUser("SELECT * FROM User WHERE id = 3 LIMIT 1");
Важно: Методы RawQuery должны возвращать непустой тип
Важно: Это доступно в Room 1.1.0-alpha3
Ответ 4
Room поддерживает аннотацию @RawQuery
для построения запросов во время выполнения.
Шаг 1: Сделать метод DAO
Пометьте метод DAO аннотацией @RawQuery
вместо обычной @RawQuery
.
@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
поддерживает возврат необработанных курсоров, сущностей, POJO и POJO со встроенными полями. -
RawQuery
поддерживает отношения
Ответ 5
Используйте SupportSQLiteQuery.
https://developer.android.com/reference/android/arch/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 и используете String вместо SupportSQLiteQuery,
вы получите ошибку:
RawQuery больше не позволяет передавать строку. Пожалуйста, используйте android.arch.persistence.db.SupportSQLiteQuery.
Использование SupportSQLiteQuery, как указано выше, решит проблему.
Примечание: убедитесь, что вы передали параметр запроса SupportSQLiteQuery, иначе вы получите эту ошибку:
Методы RawQuery должны иметь 1 и только 1 параметр с типом String или SupportSQLiteQuery
Ответ 6
Сделай это проще. Я покажу вам пример, используя предложение where с использованием двух переменных. Сделай это так:
@Query("SELECT * FROM Student WHERE stdName1= :myname AND stdId1=:myid")
List<Student> fetchAllData(String myname,int myid);
stdName1 и stdId1 являются именами столбцов
Ответ 7
@Андерсон К и @Джанки Сориано,
Я согласен с @CommonsWare,
Здесь есть некоторые ограничения в библиотеке комнат, а также мы можем написать полностью динамический запрос в базе данных комнаты с помощью @query()
поддержки SQLite База данных
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.