Liflig Document Store works on the assumption that all database tables look alike, using the jsonb
column type from Postgres to store data. So you first have to create your table like this:
CREATE TABLE example
(
-- Can have type `text` if using `StringEntityId`, or `bigint` if using `IntegerEntityId`
id uuid NOT NULL PRIMARY KEY,
created_at timestamptz NOT NULL,
modified_at timestamptz NOT NULL,
version bigint NOT NULL,
data jsonb NOT NULL
)
Then, define an entity in your application. If you use kotlinx-serialization
, the entity must be
@Serializable
.
import kotlinx.serialization.Serializable
import no.liflig.documentstore.entity.Entity
import no.liflig.documentstore.entity.UuidEntityId
@Serializable
data class ExampleEntity(
override val id: ExampleId,
val name: String,
) : Entity<ExampleId>
@Serializable
@JvmInline
value class ExampleId(override val value: UUID) : UuidEntityId {
override fun toString() = value.toString()
}
Next, implement a Repository
for your entity. See below for the implementation of
KotlinSerialization
.
import no.liflig.documentstore.entity.Versioned
import no.liflig.documentstore.repository.RepositoryJdbi
import org.jdbi.v3.core.Jdbi
class ExampleRepository(jdbi: Jdbi) :
RepositoryJdbi<ExampleId, ExampleEntity>(
jdbi,
tableName = "example",
serializationAdapter = KotlinSerialization(ExampleEntity.serializer()),
) {
fun getByName(name: String): Versioned<ExampleEntity>? {
// To implement filtering on the entity's fields, we use the getByPredicate method inherited
// from RepositoryJdbi. It lets us provide our own WHERE clause, and a lambda to bind arguments.
// In the WHERE string, we use JSON operators from Postgres (->>) to query the entity's fields.
return getByPredicate("data->>'name' = :name") {
// Binds to the :name parameter in the query
bind("name", name)
}
.firstOrNull()
}
}
This inherits CRUD methods (create
, get
, update
and delete
) from RepositoryJdbi
, so we can
use it like this:
val exampleRepo: ExampleRepository
val (entity, version) = exampleRepo.create(
ExampleEntity(
id = ExampleId(UUID.randomUUID()),
name = "test",
)
)
println("Created entity with name '${entity.name}'")
Finally, when setting up our Jdbi
instance to connect to our database, we have to install the
DocumentStorePlugin
(this registers argument types that RepositoryJdbi
depends on):
import javax.sql.DataSource
import org.jdbi.v3.core.Jdbi
import org.jdbi.v3.core.kotlin.KotlinPlugin
import org.jdbi.v3.postgres.PostgresPlugin
fun createJdbiInstance(dataSource: DataSource): Jdbi {
return Jdbi.create(dataSource)
.installPlugin(KotlinPlugin())
.installPlugin(DocumentStorePlugin())
}
The KotlinSerialization
class used in the ExampleRepository
above is an implementation of the
SerializationAdapter
interface from Liflig Document Store. This interface allows you to choose
your own JSON serialization library when using Document Store. Here is an example implementation
for kotlinx-serialization
:
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.Json
import no.liflig.documentstore.entity.Entity
import no.liflig.documentstore.repository.SerializationAdapter
private val json = Json {
encodeDefaults = true
ignoreUnknownKeys = true
}
class KotlinSerialization<EntityT : Entity<*>>(
private val serializer: KSerializer<EntityT>,
) : SerializationAdapter<EntityT> {
override fun toJson(entity: EntityT): String = json.encodeToString(serializer, entity)
override fun fromJson(value: String): EntityT = json.decodeFromString(serializer, value)
}