Reputation: 390
I have the following Model for my API Response:
@Entity(tableName = TABLE_NAME)
class WeatherEntry {
@PrimaryKey(autoGenerate = true)
var wID: Long? = null
@SerializedName("dt")
@ColumnInfo(name = COLUMN_DATE)
var date: String = ""
@SerializedName("city")
@Embedded(prefix = "location_")
var location: Location? = null
@SerializedName("main")
@Embedded(prefix = "main_")
var main: Main? = null
@SerializedName("weather")
@TypeConverters(Converters::class)
@Embedded(prefix = "weather_")
var weather: ArrayList<Weather>? = null
@SerializedName("wind")
@Embedded(prefix = "wind_")
var wind: Wind? = null
}
Weather Repo
Fetches Data from Local or Remote Data Source, I set forceRemote
to true
, because otherwise there would be no data show in the first place.
class WeatherRepository @Inject constructor(@Local var localDataSource: WeatherDataSource, @Remote var remoteDataSource: WeatherDataSource) :
WeatherDataSource {
private var caches: MutableList<WeatherEntry> = mutableListOf()
override fun getWeatherEntries(location: String, forceRemote: Boolean): Flowable<MutableList<WeatherEntry>> {
if (forceRemote) {
return refreshData(location)
} else {
return if (caches.isNotEmpty()) {
// if cache is available, return it immediately
Flowable.just(caches)
} else {
// else return data from local storage
localDataSource.getWeatherEntries(location, false)
.take(1)
.flatMap(({ Flowable.fromIterable(it) }))
.doOnNext { question -> caches.add(question) }
.toList()
.toFlowable()
.filter({ list -> !list.isEmpty() })
.switchIfEmpty(refreshData(location)) // If local data is empty, fetch from remote source instead.
}
}
}
/**
* Fetches data from remote source.
* Save it into both local database and cache.
*
* @return the Flowable of newly fetched data.
*/
private fun refreshData(location: String): Flowable<MutableList<WeatherEntry>> {
return remoteDataSource.getWeatherEntries(location,true).doOnNext({
// Clear cache
caches.clear()
// Clear data in local storage
localDataSource.deleteAllWeatherEntries()
}).flatMap(({ Flowable.fromIterable(it) })).doOnNext({ entry ->
caches.add(entry)
localDataSource.insertWeatherEntry(entry)
}).toList().toFlowable()
}
Local Data Source
class WeatherLocalDataSource @Inject constructor(private var weatherDao: WeatherDao): WeatherDataSource {
override fun insertWeatherEntry(weatherEntry: WeatherEntry) {
return weatherDao.insert(weatherEntry)
}
...
}
Remote Data Source This one definitely works as I'm getting all information from the api.
class WeatherRemoteDataSource @Inject constructor(var weatherService: WeatherService) :
WeatherDataSource {
override fun getWeatherEntries(location: String, forceRemote: Boolean): Flowable<MutableList<WeatherEntry>> {
return weatherService.getForecast(
location,
"json",
"metric",
BuildConfig.OPEN_WEATHER_MAP_API_KEY
).map(WeatherForecastResponse::weatherEntries)
}
}
DAO
@Dao
interface WeatherDao {
...
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(weatherEntry: WeatherEntry)
}
Database
@Database(
entities = [(WeatherEntry::class)],
version = 1
)
abstract class WeatherDatabase : RoomDatabase() {
abstract fun weatherDao(): WeatherDao
}
All other fields work correctly, but wID
is always null. What is wrong with my implementation?
I already tried to change it's default value to 0
and change the type to Int but that's not working either.
Upvotes: 9
Views: 11453
Reputation: 11
solutions for my problem is change type of primarykey from "long" to "Long" (java)
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "task_id") private Long taskID;
Upvotes: 1
Reputation: 192
Read https://developer.android.com/reference/androidx/room/PrimaryKey?hl=en#autoGenerate() , you'll get the answer
public boolean autoGenerate ()
Set to true to let SQLite generate the unique id.
When set to true, the SQLite type affinity for the field should be INTEGER.
If the field type is long or int (or its TypeConverter converts it to a long or int), Insert methods treat 0 as not-set while inserting the item.
If the field's type is Integer or Long (or its TypeConverter converts it to an Integer or a Long), Insert methods treat null as not-set while inserting the item.
Upvotes: 4
Reputation: 12007
Try making the id non-nullable:
@PrimaryKey(autoGenerate = true)
var wID: Long = 0
EDIT: I've found this in the sample code here. you can make your @Insert methods return the id of the inserted row object, so you could do this:
In your Dao:
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(weatherEntry: WeatherEntry) : Long
In your refresh data method:
private fun refreshData(location: String): Flowable<MutableList<WeatherEntry>> {
return remoteDataSource.getWeatherEntries(location,true).doOnNext({
// Clear cache
caches.clear()
// Clear data in local storage
localDataSource.deleteAllWeatherEntries()
}).flatMap(({ Flowable.fromIterable(it) })).doOnNext({ entry ->
val entryID = localDataSource.insertWeatherEntry(entry)
entry.wID = entryID
caches.add(entry)
}).toList().toFlowable()
}
Upvotes: 8