10

I have the following entity:

@Entity
class Foo(
    @PrimaryKey
    @ColumnInfo(name = "id")
    val id: Long,

    @ColumnInfo(name = "thing1")
    val thing1: String,

    @ColumnInfo(name = "thing2")
    val thing2: String,

    @ColumnInfo(name = "thing3")
    val thing3: String,

    @ColumnInfo(name = "thing4")
    val thing4: String
) {

    @ColumnInfo(name = "local")
    var local: String? = null

}

Where local is information that is not stored on the server, only local to the phone.

Currently when I pull information from the server GSON auto fills in my values, but since "local" does not come from the server it is not populate in that object.

Is there a way that when I call update I can have Room skip the update for the "local" column without writing a custom update to insert into all other columns except "local"? The pain point is that I could have many columns and each new column I add, I would have to add that to the custom insert statement.

I have also thought of a one-to-one mapping from the server entity to a new "local" entity, however now I have to deal with the pain of a join statement everywhere I get my entity since I need the local information.

I was hoping that I could do something like this:

@Entity
class Foo(
    @PrimaryKey
    @ColumnInfo(name = "id")
    val id: Long,

    @ColumnInfo(name = "thing1")
    val instructions: String,

    @ColumnInfo(name = "thing2")
    val instructions: String,

    @ColumnInfo(name = "thing3")
    val instructions: String,

    @ColumnInfo(name = "thing4")
    val instructions: String
) {

    @Ignore
    var local: String? = null

}

Using the @Ignore annotation, to try and ignore the local string on a generic update. Then provide a custom update statement to just save the local info

@Query("UPDATE foo SET local = :newLocal WHERE foo.id = :id")
fun updateLocal(id: Long, newLocal: String)

However ROOM seems to be smart enough to check that I used @Ignore on the local property and it will not compile with that update statement.

Any ideas?

lostintranslation
  • 23,756
  • 50
  • 159
  • 262
  • You said: _Currently when I pull information from the server GSON auto fills in my values, but since "local" does not come from the server it is not populated in that object._ Can you share how you do this? – Ferran Apr 23 '19 at 07:20
  • 1
    Would `onConflict = OnConflictStrategy.IGNORE` work? upon update, the "local" field will conflict and will be ignored – Jack Apr 23 '19 at 08:43
  • @jackz314 No it does not work. onConflict will check if primary keys are the same or not. – musooff Apr 25 '19 at 06:18

2 Answers2

10

Partial Updates got added to Room in 2.2.0

In Dao you do the following:

// Here you specify the target entity
@Update(entity = Foo::class)
fun update(partialFoo: PartialFoo)

And along your entity Foo create a PartialFoo containing the primary key and the fields you want to update.

@Entity 
class PartialFoo {
    @ColumnInfo(name = "id")
    val id: Long,

    @ColumnInfo(name = "thing1")
    val instructions: String,
}

https://stackoverflow.com/a/59834309/1724097

Rawa
  • 13,357
  • 6
  • 39
  • 55
0

Simple answer is NO. Room doesn't have conditional insertion or partial insertion.

You have to come up with your insertion logic. The best one I guess is call both database and server for data and just update your server response' local value with your database response' local value.

If you are comfortable with Rx, then you can do something like this

localDb.getFoo("id")
    .zipWith(
        remoteServer.getFoo("id"),
        BiFunction<Foo, Foo, Foo> { localFoo, remoteFoo -> 
            remoteFoo.local = localFoo.local
            remoteFoo
        }
    )

Another possible way is to write custom @Query that you insert all the values except local, but it's not feasible if you have lots of fields.

musooff
  • 6,412
  • 3
  • 36
  • 65