-
Notifications
You must be signed in to change notification settings - Fork 377
Description
FindById for R2DBC repository using embedded Id (composite key) whose property names do not match column names produces Bad-SQL-Grammar Error because the column names are not properly mapped. The reason for this seems to be the redundant handling of the embedded id property in the spring-relational.
Example:
@Table("some_table")
data class SomeEntity(
@Id
@Embedded.Nullable
val id: SomeEntityId,
val name: String
)
data class SomeEntityId(
@Column("number")
val someNumber: Long,
val someText: String
)
interface SomeR2dbcRepository : CoroutineCrudRepository< SomeEntity, SomeEntityId>Then the calling:
someR2dbcRepository.findById(SomeEntityId(1L,"a"))Produces error:
bad SQL grammar [SELECT "some_table".* FROM "some_table" WHERE "some_table".someNumber = $1 AND "some_table".someText = $2 LIMIT 2]
I was not rely sure if I'm doing it properly so I tried to debug into the spring-relational code and I find out that findById method calls getIdQuery Method in SimpleR2dbcrepository and there is some special handling of embedded Ids actually resolving the embedded propeties. That seems to be redundant and makes QueryMapper effectively unable to resolve the proper column names of the already resolved embedded properties, leaving their names as they are. Actually the QueryMapper is able to resolve embedded properties in the criteria object itself, see here. So it looks like the isEmbedded branch in the getIdQuery Method of the SimpleR2dbcrepository is not necessary and should be removed since the else branch seems to work for embedded ids as well - i tried this implementing repositry fragment with R2dbcEntityTemplate injected and it worked fine:
interface SomeR2dbcRepository : CoroutineCrudRepository< SomeEntity, SomeEntityId>, SomeEntityFragment
interface SomeEntityFragment {
suspend fun getById(id: SomeEntityId): SomeEntity?
}
class SomeEntityFragmentImpl(private val r2dbcEntityTemplate: R2dbcEntityTemplate): SomeEntityFragment {
override suspend fun getById(id: SomeEntityId): SomeEntity? {
val mappingContext = r2dbcEntityTemplate.converter.mappingContext
val idProperty: RelationalPersistentProperty = mappingContext.getRequiredPersistentEntity(SomeEntity::class.java)
.requiredIdProperty
val criteria = Criteria.where(idProperty.name).`is`(id)
return r2dbcEntityTemplate.selectOne(Query.query(criteria), SomeEntity::class.java).awaitSingle()
}
}Calling the implemented method produces proper query as expected:
Executing SQL statement [SELECT "some_table".* FROM "some_table" WHERE "some_table"."number" = $1 AND "some_table"."SOME_TEXT" = $2 LIMIT 2]