简体   繁体   English

主键字段上的 ROOM 数据库查询从 COUNT(*)[etc.] 返回 int > 0 但从 SELECT(*)[etc.] 返回 null

[英]ROOM database query on primary key field returns an int > 0 from COUNT(*)[etc.] but returned null from SELECT(*)[etc.]

we have a ROOM database with a table containing a Date field and and an amount field.我们有一个 ROOM 数据库,其中包含一个包含日期字段和金额字段的表。 The Date field is the (@NonNull) primary key.日期字段是 (@NonNull) 主键。 An initial query counts the number of records where the date <= an input date.初始查询计算日期 <= 输入日期的记录数。 If the first query returns a result > 0, a second query selects the maximum date using the same criteria, and a third query retrieves the record with that maximum date and returns the value of the amount field.如果第一个查询返回的结果 > 0,则第二个查询使用相同的条件选择最大日期,第三个查询检索具有该最大日期的记录并返回金额字段的值。 The logic behind this was: is there a date -> if so, what is the date -> use the date to find the amount.这背后的逻辑是:是否有日期 -> 如果有,日期是什么 -> 使用日期来查找金额。

the DAO contains: DAO 包含:

@Query("SELECT COUNT(*) FROM theTable WHERE date_field <= :inputDate")
int runFirstQuery (Date inputDate);

@Query("SELECT MAX(date_field) FROM theTable WHERE date_field <= :inputDate") 
Date runSecondQuery (Date inputDate);

@Query("SELECT * FROM theTable WHERE date_field = :inputDate")
TableEntity getRecord (Date inputDate);

in the activity:在活动中:

BigDecimal theAmount = BigDecimal.ZERO; 
Date theInputDate = someCalendarWhichIsntTheProblem.getTime();
int checkForRecords = theDatabase.theDAO.runFirstQuery(theInputDate);
if (checkForRecords > 0){
    Date resultDate = theDatabase.theDAO.runSecondQuery(theInputDate);
    theAmount = theDatabase.theDAO.getRecord(resultDate).getTheAmount();
}

This code has been performing correctly without incident since publication (several years now), but recently the last line referenced above threw a null pointer exception somewhere in the wild (twice, on the same day, for the same user), and I haven't been successful in duplicating the error behavior.这段代码自发布以来一直正常运行(现在已经好几年了),但最近上面引用的最后一行在野外某处抛出了 null 指针异常(两次,同一天,对于同一用户),我还没有未能成功复制错误行为。 As I understand the logic here, the NPE would be thrown only if resultDate == null, but how would that be possible since a) the date field can't contain a null value and b) we checked for matching records before running the second query?据我了解这里的逻辑,仅当 resultDate == null 时才会抛出 NPE,但这怎么可能,因为 a) 日期字段不能包含 null 值和 b) 我们在运行第二个之前检查了匹配的记录询问? There must be at least one record found by the first query in order for the second query to be executed, so what's missing here?第一个查询必须至少找到一条记录才能执行第二个查询,那么这里缺少什么?

so what's missing here?那么这里缺少什么?

The logic does not consider if the getTheAmount method retrieves null (or issues the NPE) even though the row exists, that is it appears that values, other than the date_field, could be a factor that results in the NPE.该逻辑不考虑getTheAmount方法是否检索到 null(或发出 NPE),即使该行存在,也就是说除了 date_field 之外的值似乎可能是导致 NPE 的一个因素。

Perhaps consider using:-也许考虑使用:-

@Query("SELECT coalesce((SELECT theAmount_field FROM theTable WHERE date_field=:inputDate),0.0)")
BigDecimal getTheAmount(Date inputDate);

and then instead of:-然后代替:-

BigDecimal theAmount = BigDecimal.ZERO; 
Date theInputDate = someCalendarWhichIsntTheProblem.getTime();
int checkForRecords = theDatabase.theDAO.runFirstQuery(theInputDate);
if (checkForRecords > 0){
    Date resultDate = theDatabase.theDAO.runSecondQuery(theInputDate);
    theAmount = theDatabase.theDAO.getRecord(resultDate).getTheAmount();
}

just只是

BigDecimal theAmount=theDatabase.theDao.getTheAmount(theInputDate);
  • where theAmount_field is the value/values as would be obtained by the getTheAmount method.其中theAmount_field是通过 getTheAmount 方法获得的值。
  • this would also be more efficient.这也会更有效率。

Note this is in-principle code, it has not been compiled and may therefore contain some errors.请注意,这是原理代码,尚未编译,因此可能包含一些错误。 However, the following shows the underlying query in action (albeit with integers instead of dates):-但是,以下显示了正在运行的基础查询(尽管使用整数而不是日期):-

DROP TABLE IF EXISTS theTable;
CREATE TABLE IF NOT EXISTS theTable (date_field INTEGER PRIMARY KEY, theAmount_field REAL);
INSERT OR IGNORE INTO theTable  VALUES (1,null),(2,1.1),(3,2.2),(4,3.3),(5,4.4);

SELECT coalesce((SELECT theAmount_field FROM theTable WHERE date_field=1),0.0); /*<<<<< NULL in theAmount_field */
SELECT coalesce((SELECT theAmount_field FROM theTable WHERE date_field=2),0.0);
SELECT coalesce((SELECT theAmount_field FROM theTable WHERE date_field=3),0.0);
SELECT coalesce((SELECT theAmount_field FROM theTable WHERE date_field=4),0.0);
SELECT coalesce((SELECT theAmount_field FROM theTable WHERE date_field=500),0.0); /*<<<<< no valid row */

/*Cleanup */
DROP TABLE IF EXISTS theTable;

So 5 rows are inserted with values 1-5 representing the dates.因此插入了 5 行,其中值 1-5 表示日期。 5 Queries are run. 5 查询运行。

  1. The first extracting the errant null, even though the date(sic) is found (the suspected issue not covered by the logic in the question).第一个提取错误 null,即使找到了日期(原文如此)(问题中的逻辑未涵盖的可疑问题)。 The result is that 0.0 is returned as opposed to the errant null:-结果是返回 0.0 而不是错误的 null:-

    1. 在此处输入图像描述
  2. The second returning 1.1 as exected第二次按预期返回 1.1

    1. 在此处输入图像描述
  3. The third and fourth returning 2.2 and 3.3 respectively, as expected第三个和第四个分别返回 2.2 和 3.3,符合预期

  4. The fifth uses 500 as the inputDate, for which there is no row, this returns 0.0, as expected.第五个使用 500 作为 inputDate,没有行,这按预期返回 0.0。

    1. 在此处输入图像描述

So all bases are covered (no match for the inputDate returns 0.0 and errant null in the theAmount_field) all within the single transaction.因此,所有基础都被覆盖(inputDate 的不匹配返回 0.0 和错误的 null 在 theAmount_field 中)全部在单个事务中。

If preferred the coalesce function could be replaced by the ifnull function, they, in this situation, are identical.如果愿意,合并 function 可以替换为 ifnull function,在这种情况下,它们是相同的。 See https://www.sqlite.org/lang_corefunc.html#coalesce and https://www.sqlite.org/lang_corefunc.html#ifnull参见https://www.sqlite.org/lang_corefunc.html#coalescehttps://www.sqlite.org/lang_corefunc.html#ifnull

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM