Skip to content
Advertisement

How to implement ManyToMany in r2dbc

R2DBC does not support composite keys currently. I wonder how we may implement a many-to-many relationship for now?

For example, given the two entities:

@Table
class Item(
  @Id var id: Long?,
  var title: String,
  var description: String,
)

@Table
class Tag(
  @Id
  var id: Long?,
  var title: String,
  var color: String,
)

and their schemas:

CREATE TABLE item (
    id                  SERIAL PRIMARY KEY  NOT NULL,
    title               varchar(100)        NOT NULL,
    description         varchar(500)        NOT NULL
);

CREATE TABLE tag (
    id                  SERIAL PRIMARY KEY  NOT NULL,
    title               varchar(100)        NOT NULL,
    color               varchar(6)          NOT NULL
);

I can create a table for the many-to-many mapping:

CREATE TABLE item_tag (
    item_id bigint  NOT NULL,
    tag_id  bigint  NOT NULL,
    PRIMARY KEY(item_id, tag_id)
);

But how should we define the mapping class ItemTag in kotlin/java?

@Table
class ItemTag(
  // ??????????????????????? @Id ?????????????????????
  var itemId: Long,
  var tagId: Long,
)

Or is it fine to omit the @Id? Then there cannot be any Repository for the class? I guess that would be fine. Is this the only implication?

Advertisement

Answer

There might be other ways to do this. Since CompositeKey is not supported yet in R2DBC I think. Hence, this is just one way to resolve your problem.

Data class

data class ItemTag(val itemId: Long, val tagId: Long)

Then Repository

interface TagRepository {

    fun getItemTagByTagId(tagId: Long): Flow<ItemTag>
}

Repostory Impl

@Repository
class TagRepositoryImpl(private val databaseClient: DatabaseClient) : TagRepository {
    
    override fun getItemTagByTagId(tagId: Long): Flow<ItemTag> {

        return databaseClient.sql("SELECT * FROM item_tag WHERE tag_id = :tagId")
                             .bind("tagId", tagId)
                             .map(row, _ -> rowToItemTag(row))
                             .all()
                             .flow() 
    }

    private fun rowToItemTag(row: Row): ItemTag {

        return ItemTag(row.get("item_id", Long::class.java)!!, row.get("tag_id", Long::class.java)!!)
    }
    
}

Something like that.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement