objectbox-java icon indicating copy to clipboard operation
objectbox-java copied to clipboard

Can't save same Kotlin data class multiple times with ToMany

Open HankLi0130 opened this issue 7 years ago • 7 comments

Issue Basics

  • ObjectBox version (are using the latest version?): 2.0.0
  • Reproducibility: always

Reproducing the bug

Description

I'm using ObjectBox to save data. When I save the same data with ToMany, it only save the first one.

I write a test, the size of result.details is 1.

Can I save multiple same data with ToMany? How can I fix it?

Thanks for answer.

ObjectBox(version:2.0.0) Android(target sdk version:27) Kotlin(version:1.2.60)

Code

@Test
fun orderDetailTest() {
    val orderBox: Box<Order> = store.boxFor()

    // 儲存兩個內容相同的資料
    val order = Order().apply {
        this.details.add(OrderDetail(productSpecName = "ABC"))
        this.details.add(OrderDetail(productSpecName = "ABC"))
    }

    val orderId = orderBox.put(order)

    val result = orderBox.get(orderId)

    assert(result.details.size == 2)
}

Entities

@Entity
data class Order(@Id var id: Long = 0,
                 val createTime: Long = 0,
                 val total: Int = 0) {

    @Backlink(to = "order")
    lateinit var details: ToMany<OrderDetail>
}

@Entity
data class OrderDetail(@Id var id: Long = 0,
                       val productName: String = "",
                       val productSpecName: String = "",
                       val productSpecPrice: Int = 0,
                       val quantity: Int = 0,
                       val subtotal: Int = 0) {

    lateinit var order: ToOne<Order>
}

HankLi0130 avatar Aug 15 '18 01:08 HankLi0130

I just remove the keyword 'data' with entity, it's work!

How come?

HankLi0130 avatar Aug 15 '18 01:08 HankLi0130

We'll still investigate; ping @greenrobot-team

greenrobot avatar Aug 16 '18 10:08 greenrobot

Kotlin data classes provide among others a custom implementation for hashCode(). ToMany uses a HashMap to keep track of entities to add or remove. I guess you can see where this is going:

Based on the Kotlin data class implementation if two data classes have the same properties, their hash codes match. Now given this example with two identical OrderDetail data classes according to their hashCode():

val order = Order().apply {
    this.details.add(OrderDetail(productSpecName = "ABC"))
    this.details.add(OrderDetail(productSpecName = "ABC"))
}

Two entities will be added to the ToMany list. However, the internal change tracking mechanism assumes that the second entity replaces the first one, because HashMap believes they are the same. Hence only one gets added.

Making OrderDetail a regular class drops the custom hashCode() implementation and everything works as expected. -ut

greenrobot-team avatar Aug 20 '18 13:08 greenrobot-team

Just to make this clear: this is the intended behavior, there is nothing particularly wrong here. Two data classes that share the same properties are considered to be "the same".

edit: Though I agree it is unintuitive that the data class gets added twice to the list. :/ -ut

greenrobot-team avatar Aug 20 '18 13:08 greenrobot-team

Thanks for answer.

HankLi0130 avatar Aug 21 '18 07:08 HankLi0130

@greenrobot-team I think you should reconsider this because entities could have relations to different targets which is not considered by the hashCode of data classes which would result in different entities being treated as the same entity

amgadserry avatar Oct 30 '19 22:10 amgadserry

For those who still have an issue with you could override hashCode function with ur own implementation make sure u consider all relations in ur implementation

amgadserry avatar Oct 30 '19 22:10 amgadserry