Vertx
Модуль предоставляет реализацию репозиториев на основе реактивного SQL-клиента Vert.x.
Пул соединений Vert.x работает поверх транспорта
Netty. Вы описываете интерфейс репозитория и SQL-запросы через @Repository и @Query, а Kora генерирует
реализацию, которая привязывает Vert.x Tuple, выполняет подготовленный запрос через телеметрию, преобразует
RowSet<Row> и участвует в транзакциях.
Общие правила для отображений, @Repository, @Query, @Batch, UpdateCount, макросов и других механизмов
репозиториев описаны в разделе Общие правила баз данных.
Подключение¶
Зависимость build.gradle:
Модуль:
Зависимость build.gradle.kts:
Модуль:
Также требуется предоставить реализацию драйвера Vert.x как зависимость, версии не выше
4.3.8, например
vertx-pg-client для PostgreSQL:
Для базы данных PostgreSQL, использующей аутентификацию SCRAM, также
необходимо добавить зависимость com.ongres.scram:client.
Зависимость io.projectreactor:reactor-core
требуется только если вы используете сигнатуры методов Mono/Flux.
Конфигурация¶
Основные параметры конфигурации Vert.x:
db {
connectionUri = "postgresql://localhost:5432/postgres" //(1)!
username = "postgres" //(2)!
password = "postgres" //(3)!
poolName = "kora" //(4)!
maxPoolSize = 10 //(5)!
}
- URI подключения к базе данных (
обязательный, по умолчанию не указано) - Имя пользователя для подключения (
обязательный, по умолчанию не указано) - Пароль пользователя для подключения (
обязательный, по умолчанию не указано) - Имя пула соединений (
обязательный, по умолчанию не указано) - Максимальный размер пула соединений (по умолчанию:
10)
db:
connectionUri: "postgresql://localhost:5432/postgres" #(1)!
username: "postgres" #(2)!
password: "postgres" #(3)!
poolName: "kora" #(4)!
maxPoolSize: 10 #(5)!
- URI подключения к базе данных (
обязательный, по умолчанию не указано) - Имя пользователя для подключения (
обязательный, по умолчанию не указано) - Пароль пользователя для подключения (
обязательный, по умолчанию не указано) - Имя пула соединений (
обязательный, по умолчанию не указано) - Максимальный размер пула соединений (по умолчанию:
10)
Полная конфигурация
Пример полной конфигурации, описанной в классе VertxDatabaseConfig (указаны примеры значений или значения по умолчанию):
db {
connectionUri = "postgresql://localhost:5432/postgres" //(1)!
username = "postgres" //(2)!
password = "postgres" //(3)!
poolName = "kora" //(4)!
maxPoolSize = 10 //(5)!
connectionTimeout = "10s" //(6)!
acquireTimeout = "10s" //(7)!
idleTimeout = "10m" //(8)!
cachePreparedStatements = true //(9)!
initializationFailTimeout = "10s" //(10)!
readinessProbe = false //(11)!
telemetry {
logging {
enabled = false //(12)!
}
metrics {
enabled = true //(13)!
slo = [ 1, 10, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 30000, 60000, 90000 ] //(14)!
tags = { // (15)!
"key1" = "value1"
"key2" = "value2"
}
}
tracing {
enabled = true //(16)!
attributes = { // (17)!
"key1" = "value1"
"key2" = "value2"
}
}
}
}
- URI подключения к базе данных (
обязательная, по умолчанию не указано) - Имя пользователя для подключения (
обязательная, по умолчанию не указано) - Пароль пользователя для подключения (
обязательная, по умолчанию не указано) - Имя пула соединений (
обязательная, по умолчанию не указано) - Максимальный размер пула соединений (по умолчанию:
10) - Максимальное время установления физического соединения (по умолчанию:
10s) - Максимальное время получения соединения из пула; если не задано, вместо него используется
connectionTimeout(по умолчанию не указано, необязательно) - Максимальное время простоя соединения (по умолчанию:
10m) - Кэшировать ли подготовленные выражения (по умолчанию:
true) - Максимальное время ожидания проверки соединения
SELECT 1при запуске сервиса; если не задано, проверка при запуске не выполняется (по умолчанию не указано, необязательно) - Включить ли пробу готовности для соединения с базой данных (по умолчанию:
false) - Включает логирование модуля (по умолчанию:
false) - Включает метрики модуля (по умолчанию:
true) - Настройка SLO для метрик DistributionSummary (по умолчанию:
ru.tinkoff.kora.telemetry.common.TelemetryConfig.MetricsConfig#DEFAULT_SLO) - Настройка тегов для метрик (по умолчанию:
{}) - Включает трассировку модуля (по умолчанию:
true) - Настройка атрибутов для трассировки (по умолчанию:
{})
db:
connectionUri: "postgresql://localhost:5432/postgres" #(1)!
username: "postgres" #(2)!
password: "postgres" #(3)!
poolName: "kora" #(4)!
maxPoolSize: 10 #(5)!
connectionTimeout: "10s" #(6)!
acquireTimeout: "10s" #(7)!
idleTimeout: "10m" #(8)!
cachePreparedStatements: true #(9)!
initializationFailTimeout: "10s" #(10)!
readinessProbe: false #(11)!
telemetry:
logging:
enabled: false #(12)!
metrics:
enabled: true #(13)!
slo: [ 1, 10, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 30000, 60000, 90000 ] #(14)!
tags: #(15)!
key1: value1
key2: value2
tracing:
enabled: true #(16)!
attributes: #(17)!
key1: value1
key2: value2
- URI подключения к базе данных (
обязательная, по умолчанию не указано) - Имя пользователя для подключения (
обязательная, по умолчанию не указано) - Пароль пользователя для подключения (
обязательная, по умолчанию не указано) - Имя пула соединений (
обязательная, по умолчанию не указано) - Максимальный размер пула соединений (по умолчанию:
10) - Максимальное время установления физического соединения (по умолчанию:
10s) - Максимальное время получения соединения из пула; если не задано, вместо него используется
connectionTimeout(по умолчанию не указано, необязательно) - Максимальное время простоя соединения (по умолчанию:
10m) - Кэшировать ли подготовленные выражения (по умолчанию:
true) - Максимальное время ожидания проверки соединения
SELECT 1при запуске сервиса; если не задано, проверка при запуске не выполняется (по умолчанию не указано, необязательно) - Включить ли пробу готовности для соединения с базой данных (по умолчанию:
false) - Включает логирование модуля (по умолчанию:
false) - Включает метрики модуля (по умолчанию:
true) - Настройка SLO для метрик DistributionSummary (по умолчанию:
ru.tinkoff.kora.telemetry.common.TelemetryConfig.MetricsConfig#DEFAULT_SLO) - Настройка тегов для метрик (по умолчанию:
{}) - Включает трассировку модуля (по умолчанию:
true) - Настройка атрибутов для трассировки (по умолчанию:
{})
Поскольку пул работает поверх транспорта Netty, вы также можете отдельно настроить транспорт Netty.
Использование¶
Репозиторий Vert.x объявляется интерфейсом с аннотацией @Repository и должен наследовать VertxRepository.
Каждый метод с аннотацией @Query содержит обычный SQL-запрос. Параметры метода подставляются в запрос по имени через
синтаксис :parameter, а поля объекта можно указывать через :entity.field.
@Repository
public interface EntityRepository extends VertxRepository {
record Entity(String id, @Column("value1") int field1, String value2, @Nullable String value3) {}
@Query("SELECT * FROM entities WHERE id = :id")
Mono<Entity> findById(String id);
@Query("SELECT * FROM entities")
Flux<Entity> findAll();
@Query("""
INSERT INTO entities(id, value1, value2, value3)
VALUES (:entity.id, :entity.field1, :entity.value2, :entity.value3)
""")
Mono<Void> insert(Entity entity);
@Query("""
INSERT INTO entities(id, value1, value2, value3)
VALUES (:entity.id, :entity.field1, :entity.value2, :entity.value3)
""")
Mono<UpdateCount> insertBatch(@Batch List<Entity> entities);
@Query("""
UPDATE entities
SET value1 = :entity.field1, value2 = :entity.value2, value3 = :entity.value3
WHERE id = :entity.id
""")
Mono<Void> update(Entity entity);
@Query("DELETE FROM entities WHERE id = :id")
Mono<Void> deleteById(String id);
}
@Repository
interface EntityRepository : VertxRepository {
data class Entity(val id: String, @Column("value1") val field1: Int, val value2: String, val value3: String?)
@Query("SELECT * FROM entities WHERE id = :id")
fun findById(id: String): Mono<Entity>
@Query("SELECT * FROM entities")
fun findAll(): Flux<Entity>
@Query("""
INSERT INTO entities(id, value1, value2, value3)
VALUES (:entity.id, :entity.field1, :entity.value2, :entity.value3)
""")
fun insert(entity: Entity): Mono<Void>
@Query("""
INSERT INTO entities(id, value1, value2, value3)
VALUES (:entity.id, :entity.field1, :entity.value2, :entity.value3)
""")
fun insertBatch(@Batch entities: List<Entity>): Mono<UpdateCount>
@Query("""
UPDATE entities
SET value1 = :entity.field1, value2 = :entity.value2, value3 = :entity.value3
WHERE id = :entity.id
""")
fun update(entity: Entity): Mono<Void>
@Query("DELETE FROM entities WHERE id = :id")
fun deleteById(id: String): Mono<Void>
}
SQL остается под контролем разработчика: можно использовать специфичные возможности конкретной базы данных, а Kora
занимается только безопасной подстановкой параметров, выполнением запроса и преобразованием результата.
Общие правила отображений, @Table, @Column, @Id, @Embedded, @Batch и макросов описаны в разделе
Общие правила баз данных.
Реактивные возвращаемые значения Mono и Flux являются нативными сигнатурами для этого модуля, поскольку клиент Vert.x
асинхронный. Блокирующие возвращаемые значения, такие как Entity, List<Entity>, void и UpdateCount, также
поддерживаются, но они блокируют вызывающий поток до завершения асинхронного результата, поэтому в реактивном контексте
предпочтительнее использовать реактивные сигнатуры.
Преобразование¶
Можно переопределять преобразование разных частей отображения, результата запроса и параметров запроса.
Для этого Kora предоставляет несколько интерфейсов преобразователей.
Результат¶
Используйте VertxRowSetMapper<T>, когда требуется управлять всем io.vertx.sqlclient.RowSet<Row>.
Такой преобразователь получает весь набор результата и сам решает, как его прочитать и что вернуть:
final class ResultMapper implements VertxRowSetMapper<Map<Integer, List<EntityPart>>> {
@Override
public Map<Integer, List<EntityPart>> apply(RowSet<Row> rows) {
var result = new LinkedHashMap<Integer, List<EntityPart>>(rows.size());
for (Row row : rows) {
var entityPart = new EntityPart(row.getString(0), row.getInteger(1));
var entityParts = result.computeIfAbsent(entityPart.field1(), k -> new ArrayList<>());
entityParts.add(entityPart);
}
return result;
}
}
@Repository
public interface EntityRepository extends VertxRepository {
@Mapping(ResultMapper.class)
@Query("SELECT id, value1 FROM entities")
Mono<Map<Integer, List<EntityPart>>> findAllParts();
}
class ResultMapper : VertxRowSetMapper<Map<Int, List<EntityPart>>> {
override fun apply(rows: RowSet<Row>): Map<Int, List<EntityPart>> {
val result = LinkedHashMap<Int, MutableList<EntityPart>>(rows.size())
for (row in rows) {
val entityPart = EntityPart(row.getString(0), row.getInteger(1))
result.computeIfAbsent(entityPart.field1) { ArrayList() }.add(entityPart)
}
return result
}
}
@Repository
interface EntityRepository : VertxRepository {
@Mapping(ResultMapper::class)
@Query("SELECT id, value1 FROM entities")
fun findAllParts(): Mono<Map<Int, List<EntityPart>>>
}
В большинстве случаев управлять всем RowSet<Row> не требуется.
Достаточно предоставить VertxRowMapper, и Kora автоматически адаптирует его к типу возвращаемого значения метода
с помощью встроенных вспомогательных методов VertxRowSetMapper: singleRowSetMapper (одиночный T), listRowSetMapper
(List<T>) и optionalRowSetMapper (Optional<T>). Также есть VertxRowSetMapper.extractUpdateCount, который адаптирует результат к UpdateCount.
Строка¶
Используйте VertxRowMapper<T>, когда требуется вручную преобразовать одну строку результата.
Колонки читаются из io.vertx.sqlclient.Row по типу и индексу (начиная с 0):
final class RowMapper implements VertxRowMapper<EntityPart> {
@Override
public EntityPart apply(Row row) {
return new EntityPart(row.get(String.class, 0), row.get(Integer.class, 1));
}
}
@Repository
public interface EntityRepository extends VertxRepository {
@Mapping(RowMapper.class)
@Query("SELECT id, value1 FROM entities")
Flux<EntityPart> findAllParts();
}
class RowMapper : VertxRowMapper<EntityPart> {
override fun apply(row: Row): EntityPart {
return EntityPart(row.get(String::class.java, 0), row.get(Integer::class.java, 1))
}
}
@Repository
interface EntityRepository : VertxRepository {
@Mapping(RowMapper::class)
@Query("SELECT id, value1 FROM entities")
fun findAllParts(): Flux<EntityPart>
}
Колонка¶
Используйте VertxResultColumnMapper<T>, когда требуется вручную преобразовать значение отдельной колонки по её индексу:
public final class ColumnMapper implements VertxResultColumnMapper<Entity.FieldType> {
private static final Entity.FieldType[] ALL = Entity.FieldType.values();
@Nullable
@Override
public Entity.FieldType apply(Row row, int index) {
var fieldAsInt = row.get(Integer.class, index);
if (fieldAsInt == null) {
return null;
}
for (var type : ALL) {
if (type.code() == fieldAsInt) {
return type;
}
}
return Entity.FieldType.UNKNOWN;
}
}
@Table("entities")
public record Entity(String id, @Mapping(ColumnMapper.class) @Column("value1") FieldType field1) {
enum FieldType {
UNKNOWN(-10), ONE(1), TWO(2);
private final int code;
FieldType(int code) { this.code = code; }
public int code() { return code; }
}
}
@Repository
public interface EntityRepository extends VertxRepository {
@Query("SELECT id, value1 FROM entities")
Flux<Entity> findAll();
}
class ColumnMapper : VertxResultColumnMapper<Entity.FieldType> {
override fun apply(row: Row, index: Int): Entity.FieldType? {
val fieldAsInt = row.get(Integer::class.java, index) ?: return null
return Entity.FieldType.entries.firstOrNull { it.code == fieldAsInt } ?: Entity.FieldType.UNKNOWN
}
}
@Table("entities")
data class Entity(
val id: String,
@Mapping(ColumnMapper::class) @Column("value1") val field1: FieldType
) {
enum class FieldType(val code: Int) { UNKNOWN(-10), ONE(1), TWO(2) }
}
@Repository
interface EntityRepository : VertxRepository {
@Query("SELECT id, value1 FROM entities")
fun findAll(): Flux<Entity>
}
В отличие от некоторых других модулей баз данных, здесь преобразователь колонки читает по числовому index, а не по имени колонки.
Параметр¶
Используйте VertxParameterColumnMapper<T>, когда требуется вручную преобразовать значение параметра запроса.
Преобразователь возвращает исходное значение, которое Kora привязывает в Vert.x Tuple; для значения null возвращайте null:
public final class ParameterMapper implements VertxParameterColumnMapper<Entity.FieldType> {
@Nullable
@Override
public Object apply(@Nullable Entity.FieldType fieldType) {
return (fieldType == null) ? null : fieldType.code();
}
}
@Repository
public interface EntityRepository extends VertxRepository {
@Query("UPDATE entities SET value1 = :fieldType WHERE id = :id")
UpdateCount updateFieldType(String id, @Mapping(ParameterMapper.class) Entity.FieldType fieldType);
}
class ParameterMapper : VertxParameterColumnMapper<Entity.FieldType?> {
override fun apply(fieldType: Entity.FieldType?): Any? {
return fieldType?.code
}
}
@Repository
interface EntityRepository : VertxRepository {
@Query("UPDATE entities SET value1 = :fieldType WHERE id = :id")
fun updateFieldType(id: String, @Mapping(ParameterMapper::class) fieldType: Entity.FieldType): UpdateCount
}
Преобразователь колонки результата и преобразователь параметра можно сочетать на одном поле отображения. Это удобно для преобразования, например, перечисления как при чтении строки, так и при привязке параметра:
Поддерживаемые типы¶
Список поддерживаемых типов для аргументов/возвращаемых значений из коробки
Такие типы выбраны так как поддерживаются большинством популярных баз данных.
Для них Kora предоставляет встроенные преобразователи строк, колонок и параметров.
- void
- boolean / Boolean
- short / Short
- int / Integer
- long / Long
- double / Double
- float / Float
- Buffer (
io.vertx.core.buffer.Buffer) - String
- BigInteger
- BigDecimal
- UUID
- LocalDate
- LocalDateTime
Для остальных типов используйте собственные преобразователи VertxResultColumnMapper<T> / VertxParameterColumnMapper<T>
либо VertxRowMapper<T> / VertxRowSetMapper<T>.
Транзакции¶
Для объединения запросов в транзакцию Kora предоставляет интерфейс VertxConnectionFactory
в рамках контракта VertxRepository, получаемый через getVertxConnectionFactory().
Все методы репозитория, вызванные внутри лямбды транзакции, выполняются в этой самой транзакции.
Для того чтобы выполнять запросы транзакционно, используйте inTx. Лямбда получает io.vertx.sqlclient.SqlConnection
и должна вернуть java.util.concurrent.CompletionStage<T>, поэтому реактивные результаты Mono из методов репозитория
нужно преобразовывать через .toFuture(). Если на текущем Context уже есть активная транзакция, вложенный вызов inTx
использует то же соединение и не открывает новую транзакцию.
Транзакционную последовательность операций можно оставлять внутри самого репозитория с помощью обычного метода с реализацией.
Такой подход удобен, когда нужно оставить несколько @Query-методов рядом с остальными запросами репозитория,
не вынося техническую работу с базой данных в сервисный слой.
@Repository
public interface EntityRepository extends VertxRepository {
@Query("INSERT INTO entities(id, value2) VALUES (:entity.id, :entity.value2)")
Mono<Void> insert(Entity entity);
@Query("UPDATE entities SET value2 = :value2 WHERE id = :id")
Mono<UpdateCount> updateValue(String id, String value2);
default CompletionStage<List<Entity>> saveAll(Entity one, Entity two) {
return getVertxConnectionFactory().inTx(connection ->
insert(one) //(1)!
.then(updateValue(two.id(), two.value2())) //(2)!
.thenReturn(List.of(one, two))
.toFuture());
}
}
- Будет выполнено в рамках транзакции или откатится, если вся цепочка сигнализирует об ошибке
- Будет выполнено в рамках транзакции или откатится, если вся цепочка сигнализирует об ошибке
@Repository
interface EntityRepository : VertxRepository {
@Query("INSERT INTO entities(id, value2) VALUES (:entity.id, :entity.value2)")
fun insert(entity: Entity): Mono<Void>
@Query("UPDATE entities SET value2 = :value2 WHERE id = :id")
fun updateValue(id: String, value2: String): Mono<UpdateCount>
fun saveAll(one: Entity, two: Entity): CompletionStage<List<Entity>> {
return vertxConnectionFactory.inTx { _ ->
insert(one) //(1)!
.then(updateValue(two.id, two.value2)) //(2)!
.thenReturn(listOf(one, two))
.toFuture()
}
}
}
- Будет выполнено в рамках транзакции или откатится, если вся цепочка сигнализирует об ошибке
- Будет выполнено в рамках транзакции или откатится, если вся цепочка сигнализирует об ошибке
Транзакция фиксируется при успешном завершении возвращаемого CompletionStage.
Если CompletionStage завершается с ошибкой, транзакция откатывается, а ошибка пробрасывается дальше, поэтому все изменения
в базе данных, сделанные в рамках транзакции, не применяются.
Ручное управление соединением¶
Если для запроса нужна более сложная логика или запросы вне репозитория, можно работать напрямую с io.vertx.sqlclient.SqlConnection.
Метод withConnection выполняет лямбду с соединением, но сам по себе не открывает транзакцию:
- если в текущем
Contextуже есть соединение, метод передает в лямбду это текущее соединение; - если соединения в текущем
Contextнет, метод берет новое соединение из пула, кладет его вContextна время выполнения лямбды и закрывает после завершения; - повторные вызовы
withConnectionи методы репозитория внутри этой лямбды используют то же текущее соединение.
И withConnection, и inTx возвращают CompletionStage<T>, а метод inTx построен поверх withConnection.
@Component
public final class SomeService {
private final EntityRepository repository;
public SomeService(EntityRepository repository) {
this.repository = repository;
}
public CompletionStage<List<Entity>> loadAll() {
return repository.getVertxConnectionFactory().withConnection(connection -> {
// do some work, returns CompletionStage
});
}
}
VertxConnectionFactory также предоставляет более низкоуровневые методы доступа: currentConnection() возвращает соединение,
привязанное к текущему Context (или null, если его нет), newConnection() получает новый CompletionStage<SqlConnection>
из пула, pool() возвращает исходный io.vertx.sqlclient.Pool, а telemetry() возвращает телеметрию базы данных.
Ручной запрос с телеметрией¶
Если запрос сложно выразить одним статическим @Query, можно сделать обычный метод с реализацией и самостоятельно собрать
SQL. У фабрики нет метода query; вместо этого используйте вспомогательный класс VertxRepositoryHelper, чтобы выполнить
запрос через телеметрию Kora на текущем или новом соединении.
VertxRepositoryHelper.completionStage принимает четыре аргумента:
VertxConnectionFactory(изgetVertxConnectionFactory());QueryContextс идентификатором запроса и итоговымSQL. Идентификатор попадает в телеметрию, поэтому для него удобно использовать стабильное имя видаRepository.method;- Vert.x
Tupleс привязанными значениями параметров; VertxRowSetMapper<T>, который читаетRowSet<Row>и формирует возвращаемое значение.
Если соединение уже привязано к текущему Context (например, внутри inTx или withConnection), запрос выполняется на этом
соединении; иначе новое соединение берется из пула и закрывается после завершения.
@Repository
public interface EntityRepository extends VertxRepository {
default CompletionStage<List<Entity>> findByFilter(@Nullable String name, boolean onlyActive) {
var sql = new StringBuilder("SELECT id, name FROM entities WHERE 1 = 1");
var params = new ArrayList<Object>();
if (name != null) {
params.add(name);
sql.append(" AND name = $").append(params.size());
}
if (onlyActive) {
sql.append(" AND active = true");
}
var queryContext = new QueryContext("EntityRepository.findByFilter", sql.toString());
return VertxRepositoryHelper.completionStage(
getVertxConnectionFactory(),
queryContext,
Tuple.from(params),
rows -> {
var result = new ArrayList<Entity>(rows.size());
for (var row : rows) {
result.add(new Entity(row.getString(0), row.getString(1)));
}
return result;
}
);
}
}
@Repository
interface EntityRepository : VertxRepository {
fun findByFilter(name: String?, onlyActive: Boolean): CompletionStage<List<Entity>> {
val sql = StringBuilder("SELECT id, name FROM entities WHERE 1 = 1")
val params = mutableListOf<Any>()
if (name != null) {
params += name
sql.append(" AND name = $").append(params.size)
}
if (onlyActive) {
sql.append(" AND active = true")
}
val queryContext = QueryContext("EntityRepository.findByFilter", sql.toString())
return VertxRepositoryHelper.completionStage(
vertxConnectionFactory,
queryContext,
Tuple.from(params)
) { rows ->
rows.map { row -> Entity(row.getString(0), row.getString(1)) }
}
}
}
Если вы предпочитаете реактивные типы возвращаемых значений, используйте вместо этого VertxRepositoryHelper.Reactor.mono
(возвращает Mono<T> с VertxRowSetMapper) или VertxRepositoryHelper.Reactor.flux (возвращает Flux<T> с
VertxRowMapper). Это продвинутый путь; для статических запросов предпочтительнее обычные @Query-методы.
Выборка по списку¶
Иногда требуется выборка строк по списку значений.
Kora выполняет преобразования во время компиляции и не переписывает SQL во время работы, поэтому для параметра-списка
нужен собственный преобразователь, который привязывает всю коллекцию как одно значение.
Пример ниже показывает Postgres через массив, привязываемый с помощью ANY(:ids):
public final class ListOfStringParameterMapper implements VertxParameterColumnMapper<List<String>> {
@Override
public Object apply(@Nullable List<String> value) {
return value == null ? null : value.toArray(String[]::new);
}
}
@Repository
public interface EntityRepository extends VertxRepository {
@Query("SELECT id, name FROM entities WHERE id = ANY(:ids)")
Flux<Entity> findAllByIds(@Mapping(ListOfStringParameterMapper.class) List<String> ids);
}
class ListOfStringParameterMapper : VertxParameterColumnMapper<List<String>?> {
override fun apply(value: List<String>?): Any? {
return value?.toTypedArray()
}
}
@Repository
interface EntityRepository : VertxRepository {
@Query("SELECT id, name FROM entities WHERE id = ANY(:ids)")
fun findAllByIds(@Mapping(ListOfStringParameterMapper::class) ids: List<String>): Flux<Entity>
}
Сигнатуры¶
Доступные сигнатуры для методов репозитория из коробки.
Поскольку клиент Vert.x реактивен нативно, для асинхронных сигнатур не требуется компонент Executor.
Под T подразумевается тип возвращаемого значения, либо List<T>, либо Void, либо UpdateCount.
T myMethod()@Nullable T myMethod()Optional<T> myMethod()CompletionStage<T> myMethod()Mono<T> myMethod()Project Reactor (надо подключить зависимость)Flux<T> myMethod()Project Reactor (надо подключить зависимость)
Под T подразумевается тип возвращаемого значения, либо T?, либо List<T>, либо Unit, либо UpdateCount.
myMethod(): Tsuspend myMethod(): TKotlin Coroutine (надо подключить зависимость какimplementation)myMethod(): Flow<T>Kotlin Coroutine (надо подключить зависимость какimplementation)
Телеметрия¶
Логирование, метрики и трассировка настраиваются через блок telemetry в конфигурации и описаны в разделе Справочник метрик.
Чтобы переопределить телеметрию полностью, можно предоставить собственные SPI-фабрики, подробнее в Общей документации по Базам данных.