micronaut-projects / micronaut-data

Ahead of Time Data Repositories
Apache License 2.0
459 stars 196 forks source link

Wrong entity mapping for multiple ONE_TO_MANY relations of the same entity #2996

Closed davidsonnabend closed 2 days ago

davidsonnabend commented 3 days ago

Expected Behavior

ONE_TO_MANY relations should always be mapped correctly.

Actual Behaviour

When having multiple ONE_TO_MANY relations of the same entity, the entities contains the wrong values.

Having the following data classes and repository:

@MappedEntity
data class Foo(
    @field:Id
    @field:GeneratedValue
    val id: Long? = null,

    @field:Version
    val version: Long? = null,

    @Relation(value = Relation.Kind.MANY_TO_ONE)
    val someBar: Bar? = null,

    @Relation(value = Relation.Kind.MANY_TO_ONE)
    val anotherBar: Bar? = null,
)
@MappedEntity
data class Bar(
    @field:Id
    @field:GeneratedValue
    val id: Long? = null,

    @field:Version
    val version: Long? = null,

    @Relation(value = Relation.Kind.ONE_TO_MANY, mappedBy = "someBar")
    val someFoos: List<Foo> = emptyList(),
    @Relation(value = Relation.Kind.ONE_TO_MANY, mappedBy = "anotherBar")
    val otherFoos: List<Foo> = emptyList(),
)
@JdbcRepository(dialect = Dialect.POSTGRES)
interface BarRepository : CrudRepository<Bar, Long> {

    @Join(value = "someFoos", type = Join.Type.LEFT_FETCH)
    @Join(value = "otherFoos", type = Join.Type.LEFT_FETCH)
    fun getById(id: Long): Bar
}

Only the first element of someFoos and otherFoos is mapped correctly. The other elements contains wrong values for someBar and anotherBar.

When multiple Foo instances are persisted like this:

repeat(10) {
    fooRepository.save(Foo(someBar = someBar))
}

None of the someFoos of the fetched Bar entity should have a value for anotherBar, but from the second element someBar and anotherBar are equal.

val fetchedSomeBar = barRepository.getById(someBar.id!!)
fetchedSomeBar.someFoos.forAll { foo ->
        withClue("SomeBar and AnotherBar should be different") {
            foo.someBar?.id shouldNotBe foo.anotherBar?.id
        }
}

When adding a @MappedProperty in addition to the @Relation like this:

@GeneratedValue
@MappedProperty(value = "some_bar_id")
val rawSomeBarId: Long? = null,

These id values are mapped correctly. The values for @Relation are still wrong.

Steps To Reproduce

  1. Clone example application.
  2. Run ./gradlew test.
  3. Check report of failed test and test implementation to see details.

Environment Information

Example Application

https://github.com/davidsonnabend/micronaut-data-relation-mapping-error

Version

4.5.0

davidsonnabend commented 2 days ago

@radovanradic Thank you for fixing this. I have worked my way in and wanted to ask if I am correct in adding the check you've added recently. :-)

radovanradic commented 2 days ago

@davidsonnabend Yes, I think that should be the right way. I tested this fix against your example and it was passing.

radovanradic commented 2 days ago

Closed by https://github.com/micronaut-projects/micronaut-data/pull/2999