Skip to main content

Repository modules

Repository modules use the Direct connection API, and have the same limitations.

Quick example

Let's first go over a quick example

First - configure a connection

To use SQL integration you will need a named database connection. When connecting to a SQL database, Genesis will expose its connection with name genesis. To connect to another - external - database, specify the jdbc url as a system definition in the follow format: sql.<connection name>.jdbcUrl. For example:

item("sql.my-connection.jdbcUrl", "jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1;NON_KEYWORDS=KEY,VALUE")

For full details on connection configuration settings, please see Configuration.

Next set up the repository class

After setting up a connection, or using the genesis connection, you are able to define your SQL interface. This can be done in either Kotlin or Java. The interface will need to be annotated with @RepositoryModule and a connection name provided. To interact with the database, create an annotated method like selectAllTrades() below, and provide a return type and optionally input parameters.

@RepositoryModule("my-connection")
interface TradeRepository {

@SqlQuery(namedQuery = "select * from trade")
suspend fun selectAllTrades(): List<Trade>
}

Finally inject the interface

You will then be able to inject this interface as any other class:

requestReply<Unit, Trade>("HELLO_WORLD") {
val repo = inject<TradeRepository>()

replyList {
repo.selectAllTrades()
}
}

For more examples please see Examples

Repository module reference

Let's dive a little deeper into repository modules. In the section below we will show:

Annotations

Repository modules are defined using the @RepositoryModule annotation, which specifies the name of the database connection. The interface defines methods for interacting with the database.

Class/Interface level annotation

  • @RepositoryModule - Enables sql integration on the interface
  • @RowMapper - Provides custom row mapper on return types

Method annotations

  • @SqlQuery - Used for select queries.
  • @SqlUpdate - Used for write operations (insert, update, delete).
  • @Blocking - Used to indicate blocking calls

Parameter annotations

  • @QueryParameter: Used for specifying parameter names for function parameters. Required for Java, optional for Kotlin.

Queries can be executed with or without parameters.

Return types in read operations

Below, the type parameter TYPE refers to any type supported in Direct connection API.

Blocking operations

Queries with the following return types are executed synchronously. Please note that these methods should be annotated with @Blocking to clarify the threading model.

  • TYPE - non-nullable return, that will throw a NoSuchElementException if no record is returned.
  • TYPE? - nullable return, that will return a null if no record is returned.
  • List<TYPE> - returns the full set of results. Please note that this can cause memory pressure. For larger data sets, prefer to use Flow or Flowable.

Asynchronous operations

Queries with the following return types are executed asynchronously. Please avoid making a further database call from within an asynchronous operation as these can cause deadlocks, collect first, then query.

  • Flow - Kotlin coroutines Flow<TYPE>
  • RxJava Maybe<TYPE>, Single<TYPE> and Flowable<TYPE> are supported.
  • suspend - this keyword can be added to any type in Blocking operation to turn the operation into a suspending call

Return types in write operations

The following return types are supported for write operations:

  • Unit (Kotlin) or void (Java)
  • Int (Kotlin) or int (Java)

These methods can be made asynchronous by adding the suspend keyword in Kotlin.

Parameter types

Parameters in queries should be named e.g. :my_parameter. Functions on the interface support either

  • No parameters
  • 1 or more primitives
  • a single kotlin data class/ java record class.

Primitive parameters are mapped using this priority.

  • @QueryParameter("my_parameter") - Java interfaces should use this annotation for all parameters.
  • Kotlin parameter name

When using data and record classes, the property name should match the parameter name.

Examples

Example 1: select query

@RepositoryModule("test")
interface MyInterface {
@Blocking
@SqlQuery(namedQuery = "select * from trade")
fun selectTradeList(): List<Trade>
}

A simple select query that retrieves a list of Trade objects.

Example 2: insert query with object parameter

@RepositoryModule("test")
interface MyInterface {
@Blocking
@SqlUpdate(namedQuery =
"insert into trade (id, quantity, price, status, client_id) " +
"values( :id, :quantity, :price, :status, :client_id )"
)
fun insertTrade(trade: Trade): Int
}

Insert query that takes a Trade object as a parameter.

Example 3: insert query with primitive type parameters

In Kotlin, we only need to annotate parameters whose names are not matched in the query. In Java we should always annotate parameters when using primitives.

@RepositoryModule("test")
interface MyInterface {
@Blocking
@SqlUpdate(namedQuery =
"insert into trade (id, quantity, price, status, client_id) " +
"values( :id, :quantity, :price, :status, :client_id )"
)
fun insertTrade(
id: String,
quantity: Int,
price: BigDecimal,
status: String,
@QueryParameter("client_id") clientId: String,
): Int
}