Ситуация в следующем: приложение делает REST запрос на получение данных, сохраняет их в базе, каким-то образом модифицирует, затем снова их получает с сервера. Все довольно тривиально, казалось бы, если бы не одно «но» — где мои локальные изменения?

К сожалению, Realm не поддерживает обновление данных с пропуском конкретных полей, как это было сделано в реквесте на гитхабе проекта, например

But, what about a call to specify a list of the fields that one want to overwrite (or preserve)?
copyToRealmOrUpdateOverwritingFields("field1","field2")
copyToRealmOrUpdatePreservingFields("field1","field2")
copyToRealmOrUpdate(obj).preserve("field1","field2")

https://github.com/realm/realm-java/issues/2179

что было бы довольно удобно. Давайте рассмотрим на примерах. Представим, что у нас есть чат, в котором у сообщений есть флаг «прочитано». Наш REST интерфейс не возвращает об этом состоянии никаких данных и вся информация о том, было ли сообщение прочитано или нет, хранится только локально.

В общем, после выполнения следующей строчки в коде

Флаг будет сброшен на дефолтное значение.

Вариантов несколько, некоторые из них не совсем верные, но работают, а некоторые прям вообще идеально подходят. Давайте рассмотрим два из них.

Хранение локальных изменений отдельно.

Честно говоря, сначала я придерживался именно этого подхода. Допустим, для объекта Message я создавал класс MessageData, в котором хранил флаг прочтения, дату прочтения и прочие параметры, которые не должны были перезаписываться при обновлении данных с сервера. Primary key у объектов будут одинаковые, и для отображения локальных данных надо сделать дополнительный запрос в базу. Смотрится это немного кривовато, но я лично не обнаружил на тот момент иных способов решения проблемы.

Сохранение Json (внезапно)

Отложив на какое-то время проблему в долгий ящик с предыдущим решением, я переключился на решение иных проблем (читай — одолела текучка). Когда же у меня получилось разгрести очередные завалы, я вернулся к рассматриваемой здесь проблеме — да как же так, думал я, неужели в Realm не рассмотрели никаких альтернатив.

Чем дольше я думал об этом, тем сильнее склонялся к тому, что это в общем-то логично. Посудите сами — мы можем обновлять объект с сохранением данных только в том случае, если мы его перед этим из базы получили. Иначе (согласно логике и названию метода copyToRealmOrUpdate) обновляем подходящие объекты полностью (читай — все полученные данные будем считать отныне актуальными). От верного решения меня отвлекало еще то, что используя Retrofit и какие-то промежуточные операции, я оперировал не json строкой, а массивом элементом (с которыми попутно еще что-то делал).

Ладно, не буду ходить вокруг да около. Смотрите сами.

И что мы видим в логах?

Параметр isRead сохранился, параметр ts обновился.

Магия!