i'm working with Room and suddenly it stops to show errors when i changed one or more entities. For example i add one field (vendor: String) to my Entity and it just clear all data without any error or suggestions. Just clearing all data and stop working. I don't use fallbackToDestructiveMigration.
Please help i really don't know how to avoid this. All of my branches wait for db sync.
Here is the code
#Database(entities = [(ServiceEntity::class), (ConfigEntity::class), (RequestEntity::class), (FaqEntity::class),
(SubscriptionEntity::class), (OrderEntity::class), (DeviceEntity::class), (ProblemEntity::class)], version = 5,
exportSchema = true)
abstract class RoomAppDataSource: RoomDatabase() {
abstract fun serviceDao(): ServiceDao
abstract fun configDao(): ConfigDao
abstract fun requestDao(): RequestDao
abstract fun otherDao(): OtherDao
abstract fun subscriptionsDao(): SubscriptionsDao
abstract fun ordersDao(): OrdersDao
companion object {
private val TAG = RoomAppDataSource::class.java.simpleName
private val Migration_4_5 = object: Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE ${RoomContract.TABLE_SERVICES} ADD COLUMN vendor TEXT default '' NOT NULL")
}
}
fun buildDataSource(context: Context): RoomAppDataSource = Room.databaseBuilder(
context.applicationContext, RoomAppDataSource::class.java, RoomContract.DATABASE_APP)
.addMigrations(Migration_4_5)
.build()
}
}
P.S. If i remove new line it works fine, but when i add smth in any entity app starts from tutorial page, because there are no tokens saved
Found an answer. I don't know why, but room just stop asserting new version of db or destructive migrations, so error i can see only when use Dao funcs. May be it will help to someone.
Related
I'm trying to create a Serializer for the List<Pair<String, Any>> type, I need this type for a project where I have to manipulate keys and values but user can change key names at any time and using a List of Pair is much better for what I want (and doesn't really work with a Map).
I have this code, but it produces a compiler error
class SnapshotListPairSerializer<K, V>(private val keySerializer: KSerializer<K>, private val valueSerializer: KSerializer<V>) :
KSerializer<SnapshotStateList<Pair<K, V>>> {
override val descriptor: SerialDescriptor = ListSerializer(PairSerializer(keySerializer, valueSerializer)).descriptor
override fun serialize(encoder: Encoder, value: SnapshotStateList<Pair<K, V>>) {
encoder.encodeSerializableValue(ListSerializer(PairSerializer(keySerializer, valueSerializer)), value as List<Pair<K, V>>)
}
override fun deserialize(decoder: Decoder): SnapshotStateList<Pair<K, V>> {
val list = mutableStateListOf<Pair<K, V>>()
val items = decoder.decodeSerializableValue(ListSerializer(PairSerializer(keySerializer, valueSerializer)))
list.addAll(items)
return list
}
}
Also, SnapshotStateList is a class that comes from Jetpack Compose and extends List.
The exception you get is:
Backend Internal error: Exception during IR lowering
Given that this is not providing you with meaningful information, but mentions compiler internals, this is not an error of your doing, but a bug: a cue to search for known bugs.
This seems very similar to the issue I filed on GitHub.
If it is the same cause, it should be fixed in version 1.6.10. This may explain why Philip can't repro.
P.s. the next problem you will run into is likely that Any is not registered for polymorphic serialization. Serializing Any is dodgy. If you are stuck and the documentation does not help you out, I suggest you post a new question with more information on the exact use case/expected types, and I will gladly help out.
I run into an error which I do not understand after deserializing with gson.
My usecase is way more complex, but I created this test below which shows the behaviour I see.
The issue is the calculated transient value (allObjs) that is null after deserialisation. The unit test fails on the last assert.
Apparently the #Transient prevents correct initialisation of allObjs after deserialisation.
Is this expected behaviour? Can I do anything to make the field work in this way?
If not then I have to convert all the transient fields to functions like getThemAll(), which does work.
I hope for any insights.
Regards, Rob
class CoupleDeserializeTest {
data class Couple( val objA: String, val objB: String){
#Transient
val allObjs: List<String> = listOf(objA, objB)
fun getThemAll() = listOf(objA, objB)
}
#Test
fun testDe_SerializeCouple() {
val couple = Couple("my", "text")
// This succeeds
Assert.assertNotNull(couple.allObjs)
val gson = Gson()
val json = gson.toJson(couple)
// This succeeds
Assert.assertEquals("{\"objA\":\"my\",\"objB\":\"text\"}", json)
val coupleDeserialized = gson.fromJson<Couple>(json, Couple::class.java)
// This succeeds
Assert.assertNotNull(coupleDeserialized.getThemAll())
// This fails
Assert.assertNotNull(coupleDeserialized.allObjs)
}
}
If you are marking a field as Transient, it will ignore the field from serialisation or deserialisation, snippet from JvmFlagAnnotations
/**
* Marks the JVM backing field of the annotated property as `transient`, meaning that it is not
* part of the default serialized form of the object.
*/
#Target(FIELD)
#Retention(AnnotationRetention.SOURCE)
#MustBeDocumented
public actual annotation class Transient
EDIT
It will ignore it in initialisation because, the value of objA and objB will be initialised using serialisation meaning objA and objB have not assigned by the time you allObjs gets initialised, to get the values later instead of directly assigning, you can use get()
data class Couple( val objA: String, val objB: String){
#Transient
val allObjs: List<String>
get() = listOf(objA, objB)
fun getThemAll() = listOf(objA, objB)
}
Disclaimer upfront: Recently, my interest in functional programming has grown and I've been able to apply the most basic approaches (using pure functions as much as my knowledge and working environment permits) in my job. However, I'm still very inexperienced when it comes to more advanced techniques and I thought trying to learn some by asking a question on this site might be the right idea. I've stumbled over similar issues once every while, so I think there should be patterns in FP to deal with this type of problems.
Problem description
It boils down to the following. Suppose there is an API somewhere providing a list of all possible pets.
data class Pet(val name: String, val isFavorite: Boolean = false)
fun fetchAllPetsFromApi(): List<Pet> {
// this would call a real API irl
return listOf(Pet("Dog"), Pet("Cat"), Pet("Parrot"))
}
This API knows nothing about the "favorite" field and it shouldn't. It's not under my control. It's basically just returning a list of pets. Now I want to allow users to mark pets as their favorite. And I store this flag in a local database.
So after fetching all pets from the api, I have to set the favorite flag according to the persisted data.
class FavoriteRepository {
fun petsWithUserFavoriteFlag(allPets: List<Pet>) {
return allPets.map { it.copy(isFavorite = getFavoriteFlagFromDbFor(it) }
}
fun markPetAsFavorite(pet: Pet) {
// persist to db ...
}
fun getFavoriteFlagFromDbFor(pet: Pet): Boolean {...}
}
For some reason, I think this code dealing with the problem of "fetch one part of the information from one data source, then merge it with some information from another" might benefit from the application of FP patterns, but I'm not really sure in which direction to look.
I've read some of the documentation of Arrow (great project btw :)) and am quite a Kotlin enthusiast, so answers utilizing this library would be very appreciated.
Here's something I'd potentially do. Your code has a couple of important flaws that make it unsafe from the functional programming perspective:
It doesn't flag side effects, so compiler is not aware of those and cannot track how they're are used. That means we could call those effects from anywhere without any sort of control. Examples of effects would be the network query or all the operations using the database.
Your operations don't make explicit the fact that they might succeed or fail, so callers are left to try / catch exceptions or the program will blow up. So, there's not a strong requirement to handle both scenarios, which could drive to missing some exceptions and therefore get runtime errors.
Let's try to fix it. Let's start by modeling our domain errors so we have a set of expected errors that our domain understands. Let's also create a mapper so we map all potential exceptions thrown to one of those expected domain errors, so our business logic can react to those accordingly.
sealed class Error {
object Error1 : Error()
object Error2 : Error()
object Error3 : Error()
}
// Stubbed
fun Throwable.toDomainError() = Error.Error1
As you see, we're stubbing the errors and the mapper. You can put time on designing what errors you'll need for your domain on an architecture level and write a proper pure mapper for those. Let's keep going.
Time for flagging our effects to make the compiler aware of those. To do that we use suspend in Kotlin. suspend enforces a calling context at compile time, so you cannot ever call the effect unless you're within a suspended environment or the integration point (a couroutine). We are going to flag as suspend all operations that would be a side effect here: the network request and all db operations.
I'm also taking the freedom to pull out all DB operations to a Database collaborator just for readability.
suspend fun fetchAllPetsFromApi(): List<Pet> = ...
class FavoriteRepository(private val db: Database = Database()) {
suspend fun petsWithUserFavoriteFlag(allPets: List<Pet>) {
... will delegate in the Database ops
}
}
class Database {
// This would flag it as fav on the corresponding table
suspend fun markPetAsFavorite(pet: Pet): Pet = ...
// This would get the flag from the corresponding table
suspend fun getFavoriteFlagFromDbFor(pet: Pet) = ...
}
Our side effects are safe now. They've become description of effects instead, since we cannot ever run them without providing an environment capable of running suspended effects (a coroutine or another suspended function). In functional jargon we'd say our effects are pure now.
Now, let's go for the second issue.
We also said that we were not making explicit the fact that each effect might succeed or fail, so callers might miss potential exceptions thrown and get the program blown up. We can raise that concern over our data by wrapping it with the functional Either<A, B> data type. Let's combine both ideas together:
suspend fun fetchAllPetsFromApi(): Either<Error, List<Pet>> = ...
class FavoriteRepository(private val db: Database = Database()) {
suspend fun petsWithUserFavoriteFlag(allPets: List<Pet>): Either<Error, List<Pet>> {
... will delegate in the Database ops
}
}
class Database {
// This would flag it as fav on the corresponding table
suspend fun markPetAsFavorite(pet: Pet): Either<Error, Pet> = ...
// This would get the flag from the corresponding table
suspend fun getFavoriteFlagFromDbFor(pet: Pet): Either<Error, Boolean> = ...
}
Now this makes explicit the fact that each one of those computations might succeed or fail, so the caller will be forced to handle both sides and will not forget about handling the potential errors. We're using the types in our benefit here.
Let's add the logics for the effects now:
// Stubbing a list of pets but you'd have your network request within the catch block
suspend fun fetchAllPetsFromApi(): Either<Error, List<Pet>> =
Either.catch { listOf(Pet("Dog"), Pet("Cat")) }.mapLeft { it.toDomainError() }
We can use Either#catch to wrap any suspended effects that might throw. This automatically wraps the result into Either so we can keep computing over it.
More specifically, it wraps the result of the block in Either.Right in case it succeeds, or the exception into Either.Left in case it throws. We also have mapLeft to map potential exceptions thrown (Left side) to one of our strongly typed domain errors. That is why it returns Either<Error, List<Pet>> instead of Either<Throwable, List<Pet>>.
Note that with Either we always model errors on the left side. This is by convention, since Right represents the happy path and we want our successful data there, so we can keep computing over it with map, flatMap, or whatever.
We can apply the same idea for our db methods now:
class Database {
// This would flag it as fav on the corresponding table, I'm stubbing it here for the example.
suspend fun markPetAsFavorite(pet: Pet): Either<Error, Pet> =
Either.catch { pet }.mapLeft { it.toDomainError() }
// This would get the flag from the corresponding table, I'm stubbing it here for the example.
suspend fun getFavoriteFlagFromDbFor(pet: Pet): Either<Error, Boolean> =
Either.catch { true }.mapLeft { it.toDomainError() }
}
We're stubbing the results again, but you can imagine we'd have our actual suspended effects loading from or updating the DB tables inside each Either.catch {} block above.
Finally, we can add some logic to the repo:
class FavoriteRepository(private val db: Database = Database()) {
suspend fun petsWithUserFavoriteFlag(allPets: List<Pet>): Either<Error, List<Pet>> =
allPets.map { pet ->
db.getFavoriteFlagFromDbFor(pet).map { isFavInDb ->
pet.copy(isFavorite = isFavInDb)
}
}.sequence(Either.applicative()).fix().map { it.toList() }
}
Ok this one might be a bit more complex due to how our effects are written, but I'll try to make it clear.
We need to map the list so for each pet loaded from network we can load its fav state from the Database. Then we copy it as you were doing. But given getFavoriteFlagFromDbFor(pet) returns Either<Error, Booelan> now we'd have a List<Either<Error, Pet>> as a result 🤔 That might make it hard to work with the complete list of pets, since we'd need to iterate and for each one first we'd need to check whether it's Left or Right.
To make it easier to consume the List<Pet> as a whole, we might want to swap the types here, so we'd have Either<Error, List<Pet>> instead.
To this magic, one option would be sequence. sequence requires the Either applicative in this case since that'll be used to lift the intermediate results and the final list into Either.
We're also using the chance to map the ListK into the stdlib List instead, since ListK is what sequence uses internally, but we can understand it as a functional wrapped over List in broad words, so you have an idea. Since here we're only interested on the actual list to match our types, we can map the Right<ListK<Pet>> to Right<List<Pet>>.
Finally, we can go ahead and consume this suspended program:
suspend fun main() {
val repo = FavoriteRepository()
val hydratedPets = fetchAllPetsFromApi().flatMap { pets -> repo.petsWithUserFavoriteFlag(pets) }
hydratedPets.fold(
ifLeft = { error -> println(error) },
ifRight = { pets -> println(pets) }
)
}
We're going for flatMap since we have sequential ops here.
There are potential optimizations we could do like using parTraverse to load all the fav states from DB for a list of pets in parallel and gather results in the end, but I didn't use it since I'm not sure your database is prepared for concurrent access.
Here's how you could do it:
suspend fun petsWithUserFavoriteFlag(allPets: List<Pet>): Either<Error, List<Pet>> =
allPets.parTraverse { pet ->
db.getFavoriteFlagFromDbFor(pet).map { isFavInDb ->
pet.copy(isFavorite = isFavInDb)
}
}.sequence(Either.applicative()).fix().map { it.toList() }
I think we could also simplify the whole thing a bit more by changing some of the types and how operations are structured but wasn't sure about refactoring it too much from your codebase since I'm not aware of your current team constraints.
And here's the complete codebase:
import arrow.core.Either
import arrow.core.extensions.either.applicative.applicative
import arrow.core.extensions.list.traverse.sequence
import arrow.core.extensions.listk.foldable.toList
import arrow.core.fix
import arrow.core.flatMap
data class Pet(val name: String, val isFavorite: Boolean = false)
// Our sealed hierarchy of potential errors our domain understands
sealed class Error {
object Error1 : Error()
object Error2 : Error()
object Error3 : Error()
}
// Stubbed, would be a mapper from throwable to any of the expected domain errors used via mapLeft.
fun Throwable.toDomainError() = Error.Error1
// This would call a real API irl, stubbed here for the example.
suspend fun fetchAllPetsFromApi(): Either<Error, List<Pet>> =
Either.catch { listOf(Pet("Dog"), Pet("Cat")) }.mapLeft { it.toDomainError() }
class FavoriteRepository(private val db: Database = Database()) {
suspend fun petsWithUserFavoriteFlag(allPets: List<Pet>): Either<Error, List<Pet>> =
allPets.map { pet ->
db.getFavoriteFlagFromDbFor(pet).map { isFavInDb ->
pet.copy(isFavorite = isFavInDb)
}
}.sequence(Either.applicative()).fix().map { it.toList() }
}
class Database {
// This would flag it as fav on the corresponding table, I'm stubbing it here for the example.
suspend fun markPetAsFavorite(pet: Pet): Either<Error, Pet> =
Either.catch { pet }.mapLeft { it.toDomainError() }
// This would get the flag from the corresponding table, I'm stubbing it here for the example.
suspend fun getFavoriteFlagFromDbFor(pet: Pet): Either<Error, Boolean> =
Either.catch { true }.mapLeft { it.toDomainError() }
}
suspend fun main() {
val repo = FavoriteRepository()
val hydratedPets = fetchAllPetsFromApi().flatMap { pets -> repo.petsWithUserFavoriteFlag(pets) }
hydratedPets.fold(
ifLeft = { error -> println(error) },
ifRight = { pets -> println(pets) }
)
}
School project and I'm pretty new to Android development.
The problem
I have a button with a onClick listener in a save person fragment which will save person data to the database. Everything works fine except for some reason with the first click it wont return me the inserted row ID but it will do so with 2nd click onwards.
I really need this ID in before proceeding to the next fragment.
Not sure if this is important but whenever I return (reload) to this save person fragment, the behaviour is always the same that the first click allways fails to capture the inserted row ID.
input data:
first name = John
last name = Smith
Just for demo purpose, if I will try to use this button 3x to insert the person data (returned insert ID is in the log), I will get all 3 rows in database with name John Smith, but the very first inserted row ID is not captured (default initialised value is 0), please see the log below:
Log
2020-10-19 12:49:20.320 25927-25927/ee.taltech.mobile.contacts D/TEST_ADD_PERSON_ID: insertedPersonId: 0
2020-10-19 12:49:40.153 25927-25927/ee.taltech.mobile.contacts D/TEST_ADD_PERSON_ID: insertedPersonId: 5
2020-10-19 12:49:40.928 25927-25927/ee.taltech.mobile.contacts D/TEST_ADD_PERSON_ID: insertedPersonId: 6
EDITED ORIGINAL post
As suggested in the comments, I'm trying to go about the way of using LiveData and observer, but I'm still little bit stuck.
The setup
The below is the current setup.
Entity
#Entity(tableName = "person")
data class Person(
#PrimaryKey(autoGenerate = true)
val id: Int,
DAO
#Dao
interface PersonDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addPerson(person: Person): Long
Repository
class PersonRepository(private val personDao: PersonDao) {
val readAllPersonData: LiveData<List<Person>> = personDao.readAllPersonData()
suspend fun addPerson(person: Person): Long {
return personDao.addPerson(person)
}
ViewModel
I'm not sure if I'm doing things right at all here. I broke it down here in steps and created separate variables insertedPersonILiveData and insertedPersonId.
How could pass the returned row id to insertedPersonILiveData?
class PersonViewModel(application: Application) : AndroidViewModel(application) {
var insertedPersonILiveData: LiveData<Long> = MutableLiveData<Long>()
var insertedPersonId: Long = 0L
val readAllPersonData: LiveData<List<Person>>
private val repository: PersonRepository
init {
val personDao = ContactDatabase.getDatabase(application).personDao()
repository = PersonRepository(personDao)
readAllPersonData = repository.readAllPersonData
}
suspend fun addPerson(person: Person) = viewModelScope.launch {
insertedPersonId = repository.addPerson(person)
// ****************************************************************
// insertedPersonILiveData = insertedPersonId (what to do here) ???
// ****************************************************************
}
Save person fragment
This is the way I'm calling out the addPerson via modelView.
val person = Person(0, firstName, lastName)
lifecycleScope.launch {
personViewModel.addPerson(person)
}
Log.d("TEST_ADD_PERSON_ID","insertedPersonId: ${personViewModel.insertedPersonId}")
And this is the way I have done the observer (not sure if it's even correct).
val returnedIdListener: LiveData<Long> = personViewModel.insertedPersonILiveData
returnedIdListener.observe(viewLifecycleOwner, Observer<Long> { id: Long ->
goToAddContactFragment(id)
})
private fun goToAddContactFragment(id: Long) {
Log.d("TEST_ADD_PERSON_ID", "id: " + id)
}
Create database
#Database(
entities = [Person::class, Contact::class, ContactType::class],
views = [ContactDetails::class],
version = 1,
exportSchema = false
)
abstract class ContactDatabase : RoomDatabase() {
abstract fun personDao(): PersonDao
abstract fun contactTypeDao(): ContactTypeDao
abstract fun contactDao(): ContactDao
abstract fun contactDetailsDao(): ContactDetailsDao
companion object {
// For Singleton instantiation
#Volatile
private var instance: ContactDatabase? = null
fun getDatabase(context: Context): ContactDatabase {
return instance ?: synchronized(this) {
instance ?: buildDatabase(context).also { instance = it }
}
}
private fun buildDatabase(context: Context): ContactDatabase {
return Room.databaseBuilder(context, ContactDatabase::class.java, "contacts_database")
.addCallback(
object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
WorkManager.getInstance(context).enqueue(request)
}
}
)
.build()
}
}
}
You're starting a coroutine to run addPerson, and then immediately calling Log with the current value of insertedPersonId in the viewmodel. The coroutine will run, insert the person, and update the VM with the ID of the inserted row, but that will happen long after your Log has run. Probably all of your results are actually the ID of the last record that was inserted.
I'm new to a lot of this too, but just based on what you have now, I think you just need to add
insertedPersonILiveData.value = insertedPersonId
in your addPerson function. That way you're updating that LiveData with a new value, which will be pushed to any valid observers. You've written some code that's observing that LiveData instance, so it should get the update when you set it.
edit your problem is that insertedPersonILiveData is the immutable LiveData type, so you can't set the value on it - it's read-only. You're creating a MutableLiveData object but you're exposing it as a LiveData type.
The recommended pattern for this is to create the mutable one as an internal object, expose a reference to it as an immutable type, and create a setter method that changes the value through the mutable reference (which it can access internally)
class myViewModel : ViewModel() {
// mutable version is private, all updates go through the setter function
// (the _ prefix is a convention for "private versions" of data fields)
private val _lastInsertedPersonId = MutableLiveData<Long>()
// we're making the instance accessible (for observing etc), but as
// the immutable LiveData supertype that doesn't allow setting values
val lastInsertedPersonId: LiveData<Long> = _lastInsertedPersonId
// setting the value on the MutableLiveData instance
// is done through this public function
fun setLastInsertedPersonId(id: Long) {
_lastInsertedPersonId.value = id
}
}
and then your observer would just call lastInsertedPersonId.observe, you don't need to copy the LiveData and observe that (like you're doing with returnedIdListener.
That's the basic pattern right there - internal MutableLiveData, exposed publicly as an immutable LiveData val, with a setter method to update the value. Everything outside the view model either observes the LiveData that's visible, or calls the setter method to update. Hope that makes sense! It's not that complicated once you get your head around what's basically going on
This article says that I can use Completable as return type for #Insert
But as I do that, the error occured:
error: local variable pointToInsert is accessed from within inner class; needs to be declared final
This error happens with AndoridX since Rxjava return types support included only since 2.1 version : https://issuetracker.google.com/issues/63317956#comment25
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertPoint(pointToInsert: ControlPoint): Completable
So, how to make this thing work?
As this feature is completely unavailable unless you use version 2.1+, you can actually solve this problem using lower version by making some kind of adapter for you DAO:
#Dao
interface Original {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertPoint(pointToInsert: ControlPoint)
}
class AdHocCompletableAdapter(private val dao: Original) {
fun insertPoint(pointToInsert: ControlPoint) =
Completable.create {
dao.insertPoint(pointToInsert)
it.onComplete()
}
}
Or create some more flexible solution (using, for instance, function composition).