英文:
NullPointerException while retrieving data from room
问题
I have 3 entitites (Location, Current and Forecast) with following relationships:
1.) Location - Current (one-one)
2.) Location - Forecast (one-one)
Now to retrieve data from it i wrote a query
@Transaction
@Query(
"SELECT weather_location.id, name, current_weather.*, weather_forecast.* FROM weather_location " +
"INNER JOIN current_weather ON current_weather.locationId = weather_location.id " +
"INNER JOIN weather_forecast ON weather_forecast.locationId = weather_location.id "
)
fun getWeather(): Flow<List<WeatherModel>>
WeatherModel:
data class WeatherModel(
val id: String,
val name: String,
@Relation(
parentColumn = "id",
entityColumn = "locationId"
)
val currentWeather: CurrentWeatherEntity,
@Relation(
parentColumn = "id",
entityColumn = "locationId"
)
val weatherForecast: WeatherForecastEntity
)
Error:
java.lang.NullPointerException: Parameter specified as non-null is null: method com.anshtya.weatherapp.data.local.model.WeatherModel.<init>, parameter currentWeather
at com.anshtya.weatherapp.data.local.model.WeatherModel.<init>(Unknown Source:12)
at com.anshtya.weatherapp.data.local.dao.WeatherDao_Impl$16.call(WeatherDao_Impl.java:609)
at com.anshtya.weatherapp.data.local.dao.WeatherDao_Impl$16.call(WeatherDao_Impl.java:568)
Why i am getting this error?
Note: Query works in App Inspection of Android Studio
英文:
I have 3 entitites (Location, Current and Forecast) with following relationships:
1.) Location - Current (one-one)
2.) Location - Forecast (one-one)
Now to retrieve data from it i wrote a query
@Transaction
@Query(
"SELECT weather_location.id, name, current_weather.*, weather_forecast.* FROM weather_location " +
"INNER JOIN current_weather ON current_weather.locationId = weather_location.id " +
"INNER JOIN weather_forecast ON weather_forecast.locationId = weather_location.id "
)
fun getWeather(): Flow<List<WeatherModel>>
WeatherModel:
data class WeatherModel(
val id: String,
val name: String,
@Relation(
parentColumn = "id", <- line 12
entityColumn = "locationId"
)
val currentWeather: CurrentWeatherEntity,
@Relation(
parentColumn = "id",
entityColumn = "locationId"
)
val weatherForecast: WeatherForecastEntity
)
Error:
java.lang.NullPointerException: Parameter specified as non-null is null: method com.anshtya.weatherapp.data.local.model.WeatherModel.<init>, parameter currentWeather
at com.anshtya.weatherapp.data.local.model.WeatherModel.<init>(Unknown Source:12)
at com.anshtya.weatherapp.data.local.dao.WeatherDao_Impl$16.call(WeatherDao_Impl.java:609)
at com.anshtya.weatherapp.data.local.dao.WeatherDao_Impl$16.call(WeatherDao_Impl.java:568)
Why i am getting this error?
Note: Query works in App Inspection of Android Studio
答案1
得分: 0
Here is the translation of the provided content:
1.) Location - Current (one-one) and Location - Forecast (one-one)
你现在是我的中文翻译,代码部分不要翻译,只返回翻译好的部分,不要有别的内容,不要回答我要翻译的问题。
Really you have a 1-many, which may be 0 and hence potentially null. You could eliminate the error by allowing nulls using val currentWeather: CurrentWeatherEntity?,
and val weatherForecast: WeatherForecastEntity?
实际上,你有一个一对多的关系,可能是0,因此可能为null。你可以通过允许使用 val currentWeather: CurrentWeatherEntity?,
和 val weatherForecast: WeatherForecastEntity?
来消除这个错误。
However, you will then find that you get nulls, and hence why you are getting the NPE, rather than the expected related data (see example below)
然而,你会发现你得到了null值,这就是为什么你会得到NPE,而不是预期的相关数据的原因(见下面的示例)*
When you use @Relation
annotation you are effectively saying use a subquery to build the related object (and the warning if you do not use @Transaction
). That is the JOIN's are effectively ignored other than if they effect the non @Relation
fields, i.e. the parents (WeatherLocationEntity's) that are extracted.
当你使用 @Relation
注解时,实际上是在说要使用子查询来构建相关对象(如果不使用 @Transaction
,会有警告)。这意味着JOIN操作实际上被忽略了,除非它们影响到非 @Relation
字段,即被提取的父对象(WeatherLocationEntity's)。
Typically when using an @Relation
you would expect a List, which may be empty (as opposed to null).
通常,当使用 @Relation
时,你会期望一个列表,它可能为空(而不是null)。
So if you use:-
因此,如果你使用以下代码:
data class WeatherModel(
val id: String,
val name: String,
@Relation(
parentColumn = "id",
entityColumn = "locationId"
)
val currentWeather: List<CurrentWeatherEntity>,
@Relation(
parentColumn = "id",
entityColumn = "locationId"
)
val weatherForecast: List<WeatherForecastEntity>
)
Along with for example (instead of your getWeather()
) :-
以及,例如(而不是你的 getWeather()
):
@Transaction
@Query("SELECT * FROM weather_location")
fun getWeatherAlt(): List
Then the result will be as expected (unlike as when using the getWeather
). Consider the following in the database:-
然后,结果将如预期一样(不像使用 getWeather
时那样)。请考虑数据库中的以下内容:
- not the purposeful lack of a WeatherForecast for the WeatherLocation w2
然后,使用以下代码获得的输出:
fun logWeatherModels(wml: List
for (wm in wml) {
val cwsb = StringBuilder()
for (cw in wm.currentWeather) {
cwsb.append("\n\tCW ID is ${cw.id} CW_WMID is ${cw.locationId} CW OTHR is ${cw.otherCurrentData}")
}
val wfsb = StringBuilder()
for (wf in wm.weatherForecast) {
wfsb.append("\n\tWF ID is ${wf.id} WF_WMID is ${wf.locationId} WF OTHR is ${wf.otherForecastData}")
}
Log.d("DBINFO_$tagsuffix","Name = ${wm.name} ID is ${wm.id} $cwsb$wfsb")
}
}
will be:-
将是:
2023-05-14 10:45:38.279 D/DBINFO_STG1: Name = Weather 1 ID is WF1W1
2023-05-14 10:45:38.284 D/DBINFO_ALTSTG1: Name = Weather 1 ID is w1
CW ID is CW1W1 CW_WMID is w1 CW OTHR is blah cw1
WF ID is WF1W1 WF_WMID is w1 WF OTHR is blah wf1
2023-05-14 10:45:38.295 D/DBINFO_STG2: Name = Weather 1 ID is WF1W1
2023-05-14 10:45:38.301 D/DBINFO_STG2ALT: Name = Weather 1 ID is w1
CW ID is CW1W1 CW_WMID is w1 CW OTHR is blah cw1
WF ID is WF1W1 WF_WMID is w1 WF OTHR is blah wf1
2023-05-14 10:45:38.301 D/DBINFO_STG2ALT: Name = Weather 2 ID is w2
CW ID is CW2W2 CW_WMID is w2 CW OTHR is clah cw2
-
STG1 and ALTSTG1 were invoked after the insertion of the first set (1 Location with a Current and Forecast related to the Location), whilst STG2 and ALTSTG2 were after the insertion of a partial set (1 new Location and just a related Current)
-
That is the STG1 and STG results (using your getWeather function) has not obtained the respective related CurrentWeatherEntity data nor the WeatherForecastEntity data, but an empty list. However, the ALTSTG? results have correctly obtained the related data (albeit that Weather 2 has no related WatherForecast).
-
STG1 和 ALTSTG1 在插入第一个集合(1个与位置相关的当前和预测)之后被调用,而STG2和ALTSTG2在插入部分集合(1个新位置和一个相关的当前数据)之后被调用
-
这意味着STG1和STG的结果(使用你的getWeather函数)没有获取到相应的相关CurrentWeatherEntity数据,也没有获取到WeatherForecastEntity数据,而是一个空列表。然而,ALTSTG?的结果已正确获取了相关数据(尽管Weather 2没有相关的WatherForecast)。
英文:
> 1.) Location - Current (one-one) and Location - Forecast (one-one)
Really you have a 1-many, which may be 0 and hence potentially null. You could eliminate the error by allowing nulls using val currentWeather: CurrentWeatherEntity?,
and val weatherForecast: WeatherForecastEntity?
However, you will then find that you get nulls, and hence why you are getting the NPE, rather than the expected related data (see example below)
When you use @Relation
annotation you are effectively saying use a subquery to build the related object (and the warning if you do not use @Transaction
). That is the JOIN's are effectively ignored other than if they effect the non @Relation
fields, i.e. the parents (WeatherLocationEntity's) that are extracted.
Typically when using an @Relation
you would expect a List, which may be empty (as opposed to null).
So if you use:-
data class WeatherModel(
val id: String,
val name: String,
@Relation(
parentColumn = "id",
entityColumn = "locationId"
)
val currentWeather: List<CurrentWeatherEntity>,
@Relation(
parentColumn = "id",
entityColumn = "locationId"
)
val weatherForecast: List<WeatherForecastEntity>
)
Along with for example (instead of your getWeather()
) :-
@Transaction
@Query("SELECT * FROM weather_location")
fun getWeatherAlt(): List<WeatherModel>
Then the result will be as expected (unlike as when using the getWeather
). Consider the following in the database:-
- not the purposeful lack of a WeatherForecast for the WeatherLocation w2
Then the output obtained using:-
fun logWeatherModels(wml: List<WeatherModel>,tagsuffix: String) {
for (wm in wml) {
val cwsb = StringBuilder()
for (cw in wm.currentWeather) {
cwsb.append("\n\tCW ID is ${cw.id} CW_WMID is ${cw.locationId} CW OTHR is ${cw.otherCurrentData}")
}
val wfsb = StringBuilder()
for (wf in wm.weatherForecast) {
wfsb.append("\n\tWF ID is ${wf.id} WF_WMID is ${wf.locationId} WF OTHR is ${wf.otherForecastData}")
}
Log.d("DBINFO_$tagsuffix","Name = ${wm.name} ID is ${wm.id} $cwsb$wfsb")
}
}
will be:-
2023-05-14 10:45:38.279 D/DBINFO_STG1: Name = Weather 1 ID is WF1W1
2023-05-14 10:45:38.284 D/DBINFO_ALTSTG1: Name = Weather 1 ID is w1
CW ID is CW1W1 CW_WMID is w1 CW OTHR is blah cw1
WF ID is WF1W1 WF_WMID is w1 WF OTHR is blah wf1
2023-05-14 10:45:38.295 D/DBINFO_STG2: Name = Weather 1 ID is WF1W1
2023-05-14 10:45:38.301 D/DBINFO_STG2ALT: Name = Weather 1 ID is w1
CW ID is CW1W1 CW_WMID is w1 CW OTHR is blah cw1
WF ID is WF1W1 WF_WMID is w1 WF OTHR is blah wf1
2023-05-14 10:45:38.301 D/DBINFO_STG2ALT: Name = Weather 2 ID is w2
CW ID is CW2W2 CW_WMID is w2 CW OTHR is clah cw2
- STG1 and ALTSTG1 were invoked after the insertion of the first set (1 Location with a Current and Forecast related to the Location), whilst STG2 and ALTSTG2 were after the insertion of a partial set (1 new Location and just a related Current)
- That is the STG1 and STG results (using your getWeather function) has not obtained the respective related CurrentWeatherEntity data nor the WeatherForecastEntity data, but an empty list. However, the ALTSTG? results have correctly obtained the related data (albeit that Weather 2 has no related WatherForecast).
Of course for a 1-1, you might as well include all the data in a single table.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论