Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Room doesn't autogenerate Primary Key

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.

like image 411
niclaszll Avatar asked Mar 25 '18 12:03

niclaszll


2 Answers

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()
    }
like image 151
Levi Moreira Avatar answered Sep 28 '22 08:09

Levi Moreira


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.
like image 43
chen Avatar answered Sep 28 '22 08:09

chen