英文:
Android Room @RewriteQueriesToDropUnusedColumns not working
问题
我有一个涉及表连接的 Dao 查询:
@Query("SELECT * FROM t_work t1 INNER JOIN (SELECT *, max(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;")
fun getWorkAndLog(): Flow<Map<Work, WorkLog>>
无法完成构建,报告以下错误:
E:\AndroidProjects\MyApplication\app\build\tmp\kapt3\stubs\release\com\freedom\android\work\dao\IWorkDao.java:15:
查询返回一些未被任何 [`Work`,`WorkLog`] 使用的列[max(gmt_create)]
随后提出了不同的解决方案:
- 您可以在字段上使用
@ColumnInfo
注解来指定映射。 - 您可以在方法上注释
@RewriteQueriesToDropUnusedColumns
,指示 Room 重新编写查询以避免提取未使用的列。 - 您可以通过在方法上注释
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
来抑制此警告。
我认为这三种解决方案都不是我理想的,但我想尝试第二种,看看所谓的重写会发生什么。
@RewriteQueriesToDropUnusedColumns
@Query("SELECT * FROM t_work t1 INNER JOIN (SELECT *, max(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;")
fun getWorkAndLog(): Flow<Map<Work, WorkLog>>
但它不起作用,我仍然无法构建,它仍然报告查询返回一些列
的错误,我做错了什么?
关于 Room 的依赖:
def room_version = "2.5.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// 要使用 Kotlin 注解处理工具 (kapt)
kapt "androidx.room:room-compiler:$room_version"
// 要使用 Coroutine 功能,必须将 Room 的 `ktx` artifact 作为依赖项添加。androidx.room:room-ktx:<version>
implementation "androidx.room:room-ktx:$room_version"
// 可选 - Paging 3 集成
implementation "androidx.room:room-paging:$room_version"
对于这种情况,忽略此警告是否是最佳选择?
我认为 max(gmt_create)
仅适用于此函数,不应添加到持久性类。将其留给 Room 重新编写查询 SQL,尽管我还没有看到结果,但我认为这种重写可能会改变我的意图。
首次编辑:
在我关闭 Android Studio 并重新打开后,这个编译错误消失了。所以这可能是 Android Studio 或 Gradle 的问题吗?但这个错误会不时地再次出现。
这很棘手,我无法完全复制这个问题,它只是偶尔出现。
英文:
I have a Dao query with table joins:
@Query("SELECT * FROM t_work t1 INNER JOIN (SELECT *, max(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;")
fun getWorkAndLog(): Flow<Map<Work, WorkLog>>
Unable to complete the build, it reports the following error:
E:\AndroidProjects\MyApplication\app\build\tmp\kapt3\stubs\release\com\freedom\android\work\dao\IWorkDao.java:15:
The query returns some columns [max(gmt_create)] which are not used by any of [`Work`,`WorkLog`]
Different solutions were then prompted:
- You can use
@ColumnInfo
annotation on the fields to specify the mapping. - You can annotate the method with
@RewriteQueriesToDropUnusedColumns
to direct Room to rewrite your query to avoid fetching unused columns. - You can suppress this warning by annotating the method with
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
.
I don't think any of the three solutions are ideal for me, but I'd like to try the second one and see what happens with the so-called rewrite.
@RewriteQueriesToDropUnusedColumns
@Query("SELECT * FROM t_work t1 INNER JOIN (SELECT *, max(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;")
fun getWorkAndLog(): Flow<Map<Work, WorkLog>>
But it doesn't work, I still can't build, it still reports The query returns some columns
, What I did wrong?
About Room's dependencies:
def room_version = "2.5.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// To use Kotlin annotation processing tool (kapt)
kapt "androidx.room:room-compiler:$room_version"
// To use Coroutine features, you must add `ktx` artifact from Room as a dependency. androidx.room:room-ktx:<version>
implementation "androidx.room:room-ktx:$room_version"
// optional - Paging 3 Integration
implementation "androidx.room:room-paging:$room_version"
Is it the best option to ignore this warning for this situation?
I think max(gmt_create)
exists only for this function and it should not be added to the persistence class. Leave it to Room to rewrite the query sql, although I haven't seen the results yet, I think this rewrite might change my intentions.
First Edit:
After I close Android Studio and open it again, this compilation error disappears. So this could be a problem with Android Studio or Gradle? But this error will appear again from time to time.
It's tricky, and I can't fully reproduce the problem, which only comes up occasionally.
答案1
得分: 1
查询返回了一些列[max(gmt_create)],这些列没有被[Work
, WorkLog
]中的任何一个使用
和
max(gmt_create)在某种意义上是必需的,从某种意义上说它是一个查询条件
但在输出列中并不是必需的,正如警告所说,Room并不需要它,也会忽略它。
- 也许警告消息应该是类似于“你为什么告诉我计算这个值,然后简单地把值扔掉呢”
如果不使用max函数,而是使用sum/total函数,结果就不如预期,而且会突显出潜在的缺陷。
为了演示,Work对象有以下字段/列:
- id
- name
- other
- etc
而WorkFlow对象有以下字段/列:
- id
- work_id
- gmt_create
- another
- blah
以下将创建两个表并加载一些数据,使用SQLite工具(而不是在演示应用程序中编写所有代码)
CREATE TABLE IF NOT EXISTS t_work (id INTEGER PRIMARY KEY, name TEXT, other TEXT, etc TEXT);
CREATE TABLE IF NOT EXISTS t_work_log (id INTEGER PRIMARY KEY, work_id INTEGER, gmt_create INTEGER, another TEXT, blah TEXT);
INSERT INTO t_work VALUES
(1,'w1','otherw1','etcw1'),
(2,'w2','otherw2','etcw2'),
(3,'w3','otherw3','etcw3')
;
INSERT INTO t_work_log (work_id,gmt_create,another,blah) VALUES
(1,10,900,'blah for w1-10-900')
,(1,11,901,'blah for w1-11-901')
,(1,8,902,'blah for w1-8-902')
,(1,7,903,'blah for w1-7-903')
,(1,12,904,'blah for w1-12-904')
,(1,1,905,'blah for w1-1-905')
,(1,21,906,'blah for w1-21-906')
,(1,6,907,'blah for w1-6-907')
,(2,111,801,'blah for w2-111-801')
,(2,18,802,'blah for w2-18-802')
,(2,17,803,'blah for w2-17-803')
,(2,112,804,'blah for w2-112-804')
,(2,11,805,'blah for w2-11-805')
,(2,121,806,'blah for w2-121-806')
,(2,16,807,'blah for w2-16-807')
,(3,211,701,'blah for w3-211-701')
,(3,28,702,'blah for w3-28-702')
,(3,27,703,'blah for w3-27-703')
,(3,212,704,'blah for w3-27-704')
,(3,21,705,'blah for w3-21-705')
,(3,221,706,'blah for w3-221-706')
,(3,26,707,'blah for w3-26-707')
;
现在你的查询**SELECT * FROM t_work t1 INNER JOIN (SELECT *, max(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;
**的结果是:
Room将执行它的操作,忽略max(gmt_create)列,并使用gmt_create列的显然正确的值来为gmt_create字段赋值。除了警告之外,没有发生异常。
现在,如果查询是***SELECT * FROM t_work t1 INNER JOIN (SELECT *, sum(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;
***,结果将是:
gmt_create列和sum(gmt_create)不同,Room将gmt_create列的值分配给gmt_create字段,并且这个缺陷会变得明显。
为了避免警告并正确获得派生值,可以选择以下两种选项:
- a) 使用一个POJO,该POJO具有一个字段,Room将派生值分配给该字段(例如,一个名为
max(gmt_create)
的字段)。- 在这种情况下,你的查询是正确的。
- b) 让查询操作值以生成适当的列和值,允许Room根据要求分配适当的值。
- 在这种情况下,基于演示的查询可以是*
SELECT t1.*,t2.id,t2.work_id,t2.[max(gmt_create)] AS gmt_create,t2.another,t2.blah FROM t_work t1 INNER JOIN (SELECT *, max(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;
*。
- 在这种情况下,基于演示的查询可以是*
后一个查询将输出:
如果使用sum而不是max,则输出将是:
也就是说,它清晰地表明派生值已被放入gmt_create列中,并且没有输出多余的列,从而避免了警告和对警告的抑制需求。
-
但请注意,这样做突显了另一个因素,即在一个组中对其他列分配了任意值。
附加
关于你的答案,我最好的选择就是简单地忽略这个警告。但我对@RewriteQueriesToDropUnusedColumns文档的疑问是,该注释会导致Room重新编写我的查询方法,它是如何被重新编写的?它只是从我的SQL语句中删除max(gmt_create)吗?这将导致查询结果不正确。
- 简单地忽略这个警告是最简单的。
2
英文:
> The query returns some columns [max(gmt_create)] which are not used by any of [Work
,WorkLog
]
and
> max(gmt_create) is required, in a sense it is a query condition
It is not required in the output columns though AND as the warning says Room does not need it and ignores it.
- Perhaps the warning message should be along the lines of "Why are you telling me to work out this value for you to simply throw the value in the bin"
Now if instead of using the max function, you were to use the sum/total function then the result would not be as expected and would highlight the underlying flaw.
To demonstrate a Work object has the fields/columns
- id
- name
- other
- etc
and the WorkFlow object has the fields(columns)
- id
- work_id
- gmt_create
- another
- blah
So the following will create the 2 tables and load some data into them, using an SQLite tool (rather than writing all the code in a demo App)
CREATE TABLE IF NOT EXISTS t_work (id INTEGER PRIMARY KEY, name TEXT, other TEXT, etc TEXT);
CREATE TABLE IF NOT EXISTS t_work_log (id INTEGER PRIMARY KEY, work_id INTEGER, gmt_create INTEGER, another TEXT, blah TEXT);
INSERT INTO t_work VALUES
(1,'w1','otherw1','etcw1'),
(2,'w2','otherw2','etcw2'),
(3,'w3','otherw3','etcw3')
;
INSERT INTO t_work_log (work_id,gmt_create,another,blah) VALUES
(1,10,900,'blah for w1-10-900')
,(1,11,901,'blah for w1-11-901')
,(1,8,902,'blah for w1-8-902')
,(1,7,903,'blah for w1-7-903')
,(1,12,904,'blah for w1-12-904')
,(1,1,905,'blah for w1-1-905')
,(1,21,906,'blah for w1-21-906')
,(1,6,907,'blah for w1-6-907')
,(2,111,801,'blah for w2-111-801')
,(2,18,802,'blah for w2-18-802')
,(2,17,803,'blah for w2-17-803')
,(2,112,804,'blah for w2-112-804')
,(2,11,805,'blah for w2-11-805')
,(2,121,806,'blah for w2-121-806')
,(2,16,807,'blah for w2-16-807')
,(3,211,701,'blah for w3-211-701')
,(3,28,702,'blah for w3-28-702')
,(3,27,703,'blah for w3-27-703')
,(3,212,704,'blah for w3-27-704')
,(3,21,705,'blah for w3-21-705')
,(3,221,706,'blah for w3-221-706')
,(3,26,707,'blah for w3-26-707')
;
Now your query SELECT * FROM t_work t1 INNER JOIN (SELECT *, max(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;
results in :-
Room will do it's stuff, IGNORING, the max(gmt_create) column, and using the apparently correct value from the gmt_create column to assign a value to the gmt_create field. Other than the warning nothing untoward happens.
Now if the query were SELECT * FROM t_work t1 INNER JOIN (SELECT *, sum(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;
the result would be:-
The gmt_create column and the sum(gmt_create) differ, Room will assign the gmt_create column's value to the gmt_create field and the flaw will be noticable.
The options to avoid the warning AND to correctly get the derived value should be to either
- a) Use a POJO that has a field that Room will assigned the derived value to (e.g. a field name
max(gmt_create)
).- in which case your query would be fine.
- b) Have the query manipulate the values to produce output with suitable columns and values that will allow Room to assign appropriate values according to the requirements.
- in which case the query based upon the demo could be
SELECT t1.*,t2.id,t2.work_id,t2.[max(gmt_create)] AS gmt_create,t2.another,t2.blah FROM t_work t1 INNER JOIN (SELECT *, max(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;
- in which case the query based upon the demo could be
The latter query would output:-
If using sum instead of max then it would output:-
i.e. it is a) clear that the derived values have been placed into the gmt_create column ADN that the extraneous column has not been output, thus avoiding the warning and any need to suppress the warning.
-
But note that then another factor is highlighted and that is that arbitrary values are assigned to other columns in a group.
Additional
re:-
> After reading your answer, the best option in my case would have been to simply ignore the warning. But I'm curious about the @RewriteQueriesToDropUnusedColumns documentation that says annotation will cause Room to rewrite your Query methods, how is it rewritten? Does it just remove max(gmt_create) from my sql statement? This will result in incorrect query results.
-
Simplest is to ignore the warning.
-
According to a simple testRegarding the rest does it re-write the query BUT by embedding the original query as a subquery.
Consider:-
@Transaction
@Query("SELECT *, 'useless data' AS useless_column FROM job")
fun getAllJobsEtcWithUselessData(): List<JobWithWorkersAndPosterWithPosterWorker>
and thus:-
warning: The query returns some columns [useless_column] which are not used by a.a.so75656155kotlinroomrelationships.JobWithWorkersAndPosterWithPosterWorker. You can use @ColumnInfo annotation on the fields to specify the mapping. You can annotate the method with @RewriteQueriesToDropUnusedColumns to direct Room to rewrite your query to avoid fetching unused columns. You can suppress this warning by annotating the method with @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH). Columns returned by the query: job_id, jobName, useless_column.
public abstract java.util.List<a.a.so75656155kotlinroomrelationships.JobWithWorkersAndPosterWithPosterWorker> getAllJobsEtcWithUselessData();
The generated java (in the java(generated) from Android View) includes:-
@Override
public List<JobWithWorkersAndPosterWithPosterWorker> getAllJobsEtcWithUselessData() {
final String _sql = "SELECT *, 'useless data' AS useless_column FROM job";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
....
Change to add the @Rewrite.... :-
@Transaction
@Query("SELECT *, 'useless data' AS useless_column FROM job")
@RewriteQueriesToDropUnusedColumns
fun getAllJobsEtcWithUselessData(): List<JobWithWorkersAndPosterWithPosterWorker>
The warning is gone and the generated java is now :-
@Override
public List<JobWithWorkersAndPosterWithPosterWorker> getAllJobsEtcWithUselessData() {
final String _sql = "SELECT `job_id`, `jobName` FROM (SELECT *, 'useless data' AS useless_column FROM job)";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
....
Actually quite a nice little trick. HOWEVER, as explained re sum v max the actual derived value is not available/used.
Now if you still have concerns, then you can do away with the @RewriteQueriesToDropUnusedColumns
by:-
- Include the annotation appropriately.
- Compile.
- Go to the java (generated)
- Find the class that is the same name as the respective @Dao annotated interface/abstract class but suffixed with Impl
- search for the function, the adjusted SQL can then be used to do what Room does and that is embed the original query as a subquery, and have the overall query only extract the required columns.
答案2
得分: 0
这个查询
SELECT * FROM t_work t1 INNER JOIN (SELECT *, max(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;
存在语法错误。
尝试这个
SELECT * FROM t_work t1 INNER JOIN
( SELECT work_id, MAX(gmt_create) Amax_gmt_create FROM t_work_log GROUP BY work_id)
t2 ON t1.id = t2.work_id;
英文:
I This query
SELECT * FROM t_work t1 INNER JOIN (SELECT *, max(gmt_create) FROM t_work_log GROUP BY work_id) t2 ON t1.id = t2.work_id;
have a syntax error.
Try it
SELECT * FROM t_work t1 INNER JOIN
( SELECT work_id, MAX(gmt_create) Amax_gmt_create FROM t_work_logGROUP BY work_id)
t2 ON t1.id = t2.work_id;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论