Skip to content

Result mapping of projections should always use the DTO and not the domain entity #1554

Closed
@RobertHeim

Description

@RobertHeim

TL;DR: Could the result mapping strategy for DTO projections be changed to always use the DTO and not the domain entity? That would save us a lot of error prone and repetitive work, because in Kotlin null-safety prohibits instantiating the domain entities first, if not all non-null fields are also selected in the query.

Details

From the docs I get that result mapping of DTO projections basically consists of these two facts:

  • select only the columns that the DTO defines as attributes
  • instantiate the domain entity and then map the domain entity to the DTO.

Effectively this means that the instantiated domain entity sets all fields null that were not queried. This works in Java, but not in Kotlin, because of null-safety.

For example:

data class Avatar(
  @Id
  val id: UUID? = null,
  val userId: Long,
  val avatar: String
)

data class AvatarIdWithUserId(
  val id: UUID,
  val userId: Long,
)

@Repository
interface AvatarRepository : CoroutineCrudRepository<Avatar, UUID> {
  fun findByUserIdIn(ids: Set<Long>): Flow<AvatarIdWithUserId>
}

If there is an avatar for userId 1L the query findByUserIdIn(setOf(1L)) fails with

Caused by: java.lang.NullPointerException: Parameter specified as non-null is null: method my.package.Avatar.<init>, parameter avatar

From the docs I understand that to make that work it would require me to write a @Query annotation, because then the result mapping uses the DTO immediately:

  @Query("SELECT id, user_id FROM avatar WHERE user_id IN (:ids)")
  fun findByUserIdIn(ids: Set<Long>): Flow<AvatarIdWithUserId>

Obviously, that is error prone and unnecessary work. For instance, a major issue in this example is that the query would fail if ids is empty. Hence, the caller side must first guarantee that ids is non empty - which is cumbersome. Hence, it would require us to provide a custom implementation for that simple query to return if ids is empty.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions