Browse Source

Update Java SDK example and documentation KIKIMR-14232

Add run options for Java SDK example
Update Java SDK example documentation
Update simple project
Fix broken link to cpp example
Update simple project example
Update basic example

ref:287f21d20bb80e5667f2c8f295c06dab0ed351ae
alexandr268 2 years ago
parent
commit
bcc94a11eb

+ 1 - 0
ydb/docs/ru/core/getting_started/_includes/sdk.md

@@ -10,5 +10,6 @@
 
    * [Python](../../reference/ydb-sdk/example/python/index.md)
    * [Go](../../reference/ydb-sdk/example/go/index.md)
+   * [Java](../../reference/ydb-sdk/example/java/index.md)
 
 Полная информация о {{ ydb-short-name }} SDK находится в разделе [Работа с {{ ydb-short-name }} SDK](../../reference/ydb-sdk/index.md).

+ 9 - 9
ydb/docs/ru/core/reference/ydb-sdk/example/_includes/index.md

@@ -7,7 +7,7 @@
 {% endif %}
 - [С# (.NET)](../example-dotnet.md)
 - [Go](../go/index.md)
-- [Java](../example-java.md)
+- [Java](../java/index.md)
 - [Node.js](../example-nodejs.md)
 - [Python](../python/index.md)
 
@@ -15,35 +15,35 @@
 
 {% include [init.md](steps/01_init.md) %}
 
-{% if oss %}[C++](../example-cpp.md#init) | {% endif %} [C# (.NET)](../example-dotnet.md#init) | [Go](../go/index.md#init) | [Java](../example-java.md#init) | Node.js | [PHP](../example-php.md#init) | [Python](../python/index.md#init)
+{% if oss %}[C++](../example-cpp.md#init) | {% endif %} [C# (.NET)](../example-dotnet.md#init) | [Go](../go/index.md#init) | [Java](../java/index.md#init) | Node.js | [PHP](../example-php.md#init) | [Python](../python/index.md#init)
 
 {% include [create_table.md](steps/02_create_table.md) %}
 
-{% if oss %}[C++](../example-cpp.md#create-table) | {% endif %} [C# (.NET)](../example-dotnet.md#create-table) | [Go](../go/index.md#create-table) | [Java](../example-java.md#create-table) | Node.js | PHP | [Python](../python/index.md#create-table)
+{% if oss %}[C++](../example-cpp.md#create-table) | {% endif %} [C# (.NET)](../example-dotnet.md#create-table) | [Go](../go/index.md#create-table) | [Java](../java/index.md#create-table) | Node.js | PHP | [Python](../python/index.md#create-table)
 
 {% include [write_queries.md](steps/03_write_queries.md) %}
 
-{% if oss %}[C++](../example-cpp.md#write-queries) | {% endif %} [C# (.NET)](../example-dotnet.md#write-queries) | Go | [Java](../example-java.md#write-queries) | Node.js | PHP | [Python](../python/index.md#write-queries)
+{% if oss %}[C++](../example-cpp.md#write-queries) | {% endif %} [C# (.NET)](../example-dotnet.md#write-queries) | Go | [Java](../java/index.md#write-queries) | Node.js | PHP | [Python](../python/index.md#write-queries)
 
 {% include [query_processing.md](steps/04_query_processing.md) %}
 
-{% if oss %}[C++](../example-cpp.md#query-processing) |  {% endif %} [C# (.NET)](../example-dotnet.md#query-processing) | [Go](../go/index.md#query-processing) | [Java](../example-java.md#query-processing) | Node.js | PHP | [Python](../python/index.md#query-processing)
+{% if oss %}[C++](../example-cpp.md#query-processing) |  {% endif %} [C# (.NET)](../example-dotnet.md#query-processing) | [Go](../go/index.md#query-processing) | [Java](../java/index.md#query-processing) | Node.js | PHP | [Python](../python/index.md#query-processing)
 
 {% include [param_queries.md](steps/06_param_queries.md) %}
 
-{% if oss %}[C++](../example-cpp.md#param-queries) |  {% endif %} [C# (.NET)](../example-dotnet.md#param-queries) | [Go](../go/index.md#param-queries) | [Java](../example-java.md#param-queries) | Node.js | PHP | [Python](../python/index.md#param-queries)
+{% if oss %}[C++](../example-cpp.md#param-queries) |  {% endif %} [C# (.NET)](../example-dotnet.md#param-queries) | [Go](../go/index.md#param-queries) | [Java](../java/index.md#param-queries) | Node.js | PHP | [Python](../python/index.md#param-queries)
 
 {% include [scan_query.md](steps/08_scan_query.md) %}
 
-{% if oss %}C++ |  {% endif %} [C# (.NET)](../example-dotnet.md#scan-query) | [Go](../go/index.md#scan-query) | [Java](../example-java.md#scan-query) | [Node.js](../example-nodejs.md#scan-query) | PHP | [Python](../python/index.md#scan-query)
+{% if oss %}C++ |  {% endif %} [C# (.NET)](../example-dotnet.md#scan-query) | [Go](../go/index.md#scan-query) | [Java](../java/index.md#scan-query) | [Node.js](../example-nodejs.md#scan-query) | PHP | [Python](../python/index.md#scan-query)
 
 {% include [multistep_transactions.md](steps/09_multistep_transactions.md) %}
 
-{% if oss %}[C++](../example-cpp.md#multistep-transactions) |  {% endif %} C# (.NET) | Go | [Java](../example-java.md#multistep-transactions) | Node.js | PHP | Python
+{% if oss %}[C++](../example-cpp.md#multistep-transactions) |  {% endif %} C# (.NET) | Go | [Java](../java/index.md#multistep-transactions) | Node.js | PHP | Python
 
 {% include [transaction_control.md](steps/10_transaction_control.md) %}
 
-{% if oss %}[C++](../example-cpp.md#tcl) |  {% endif %} C# (.NET) | Go | [Java](../example-java.md#tcl) | Node.js | PHP | [Python](../python/index.md#tcl)
+{% if oss %}[C++](../example-cpp.md#tcl) |  {% endif %} C# (.NET) | Go | [Java](../java/index.md#tcl) | Node.js | PHP | [Python](../python/index.md#tcl)
 
 {% include [error_handling.md](steps/50_error_handling.md) %}
 

+ 0 - 2
ydb/docs/ru/core/reference/ydb-sdk/example/example-java.md

@@ -1,2 +0,0 @@
-{% include [example-java.md](_includes/example-java.md) %}
-

+ 23 - 0
ydb/docs/ru/core/reference/ydb-sdk/example/java/_includes/run_custom.md

@@ -0,0 +1,23 @@
+Для выполнения примера с использованием любой доступной базы данных YDB вам потребуется знать [Эндпоинт](../../../../../concepts/connect.md#endpoint) и [Размещение базы данных](../../../../../concepts/connect.md#database).
+
+Если в базе данных включена аутентификация, то вам также понадобится выбрать [режим аутентификации](../../../../../concepts/connect.md#auth-modes) и получить секреты - токен или логин/пароль.
+
+Выполните команду по следующему образцу:
+
+``` bash
+<auth_mode_var>="<auth_mode_value>" java -jar examples/simple_project/target/ydb-simple-project.jar <endpoint>?database=<database>
+```
+
+, где
+
+- `<endpoint>` - [Эндпоинт](../../../../../concepts/connect.md#endpoint)
+- `<database>` - [Размещение базы данных](../../../../../concepts/connect.md#database) 
+- `<auth_mode_var`> - [Переменная окружения](../../../auth.md#env), определяющая режим аутентификации
+- `<auth_mode_value>` - Значение параметра аутентификации для выбранного режима
+
+Например:
+``` bash
+YDB_ACCESS_TOKEN_CREDENTIALS="t1.9euelZqOnJuJlc..." java -jar examples/simple_project/target/ydb-simple-project.jar grpcs://ydb.example.com:2135?database=/somepath/somelocation
+```
+
+{% include [../../_includes/pars_from_profile_hint.md](../../_includes/pars_from_profile_hint.md) %}

+ 5 - 0
ydb/docs/ru/core/reference/ydb-sdk/example/java/_includes/run_docker.md

@@ -0,0 +1,5 @@
+Для соединения с развернутой локальной базой данных YDB по сценарию [Docker](../../../../../getting_started/self_hosted/ydb_docker.md) в конфигурации по умолчанию  выполните следующую команду:
+
+``` bash
+YDB_ANONYMOUS_CREDENTIALS=1 java -jar examples/simple_project/target/ydb-simple-project.jar grpc://localhost:2136?database=/local
+```

+ 11 - 0
ydb/docs/ru/core/reference/ydb-sdk/example/java/_includes/run_options.md

@@ -0,0 +1,11 @@
+{% list tabs %}
+
+- Local Docker
+
+  {% include [run_docker.md](run_docker.md) %}
+
+- Любая база данных
+
+  {% include [run_custom.md](run_custom.md) %}
+
+{% endlist %}

+ 369 - 0
ydb/docs/ru/core/reference/ydb-sdk/example/java/index.md

@@ -0,0 +1,369 @@
+# Приложение на Java
+
+На этой странице подробно разбирается код [тестового приложения](https://github.com/yandex-cloud/ydb-java-sdk/tree/master/examples/simple_project), доступного в составе [Java SDK](https://github.com/yandex-cloud/ydb-java-sdk) {{ ydb-short-name }}.
+
+## Скачивание SDK и запуск примера {#download}
+
+Приведенный ниже сценарий запуска использует [git](https://git-scm.com/downloads) и [Maven](https://maven.apache.org/download.html). 
+
+Создайте рабочую директорию и выполните в ней из командной строки команду клонирования репозитория с github.com:
+
+``` bash
+git clone https://github.com/yandex-cloud/ydb-java-sdk
+```
+
+Далее выполните сборку SDK и входящих в него примеров
+
+``` bash
+cd ydb-java-sdk && mvn clean package
+```
+
+Далее из этой же рабочей директории выполните команду запуска тестового приложения, которая будет отличаться в зависимости от того, к какой базе данных необходимо подключиться.
+
+{% include [run_options.md](_includes/run_options.md) %}
+
+
+{% include [init.md](../_includes/steps/01_init.md) %}
+
+Основные параметры инициализации драйвера
+* Cтрока подключения с информацией об [эндпоинте](../../../../concepts/connect.md#endpoint) и [базе данных](../../../../concepts/connect.md#database). Единственный обязательные параметр.
+* Провайдер [аутенфикации](../../auth.md##auth-provider). В случае отсутсвия прямого указания - будет использоваться [анонимное подключение](../../../../concepts/connect.md#auth-modes).
+* Настройки [пула сессий](../../recipes/session_pool_limit/index.md)
+
+Фрагмент кода приложения для инициализации драйвера:
+
+```java
+GrpcTransport transport = GrpcTransport.forConnectionString(connectionString)
+        .withAuthProvider(CloudAuthHelper.getAuthProviderFromEnviron())
+        .build();
+GrpcTableRpc rpc = GrpcTableRpc.ownTransport(transport);
+this.tableClient = TableClient.newClient(rpc).build();
+```
+
+Все операции с YDB рекомендуется выполнять с помощью класса-хелпера `SessionRetryContext`, который обеспечивает корректное повтороное выполнение операция в случае частичной недоступности. Фрагмент кода для инициализации контекста ретраев:
+
+```java
+this.retryCtx = SessionRetryContext.create(tableClient).build();
+```
+
+{% include [create_table.md](../_includes/steps/02_create_table.md) %}
+
+Для создания таблиц используется метод `Session.createTable()`:
+
+```java
+private void createTables() {
+    TableDescription seriesTable = TableDescription.newBuilder()
+        .addNullableColumn("series_id", PrimitiveType.uint64())
+        .addNullableColumn("title", PrimitiveType.utf8())
+        .addNullableColumn("series_info", PrimitiveType.utf8())
+        .addNullableColumn("release_date", PrimitiveType.date())
+        .setPrimaryKey("series_id")
+        .build();
+
+    retryCtx.supplyStatus(session -> session.createTable(database + "/series", seriesTable))
+            .join().expect("create table problem");
+
+    TableDescription seasonsTable = TableDescription.newBuilder()
+        .addNullableColumn("series_id", PrimitiveType.uint64())
+        .addNullableColumn("season_id", PrimitiveType.uint64())
+        .addNullableColumn("title", PrimitiveType.utf8())
+        .addNullableColumn("first_aired", PrimitiveType.date())
+        .addNullableColumn("last_aired", PrimitiveType.date())
+        .setPrimaryKeys("series_id", "season_id")
+        .build();
+
+    retryCtx.supplyStatus(session -> session.createTable(database + "/seasons", seasonsTable))
+            .join().expect("create table problem");
+
+    TableDescription episodesTable = TableDescription.newBuilder()
+        .addNullableColumn("series_id", PrimitiveType.uint64())
+        .addNullableColumn("season_id", PrimitiveType.uint64())
+        .addNullableColumn("episode_id", PrimitiveType.uint64())
+        .addNullableColumn("title", PrimitiveType.utf8())
+        .addNullableColumn("air_date", PrimitiveType.date())
+        .setPrimaryKeys("series_id", "season_id", "episode_id")
+        .build();
+
+    retryCtx.supplyStatus(session -> session.createTable(database + "/episodes", episodesTable))
+            .join().expect("create table problem");
+}
+```
+
+С помощью метода `Session.describeTable()` можно вывести информацию о структуре таблицы и убедиться, что она успешно создалась:
+
+```java
+private void describeTables() {
+    logger.info("--[ DescribeTables ]--");
+
+    Arrays.asList("series", "seasons", "episodes").forEach(tableName -> {
+        String tablePath = database + '/' + tableName;
+        TableDescription tableDesc = retryCtx.supplyResult(session -> session.describeTable(tablePath))
+                .join().expect("describe table problem");
+
+        List<String> primaryKeys = tableDesc.getPrimaryKeys();
+        logger.info("  table {}", tableName);
+        for (TableColumn column : tableDesc.getColumns()) {
+            boolean isPrimary = primaryKeys.contains(column.getName());
+            logger.info("     {}: {} {}", column.getName(), column.getType(), isPrimary ? " (PK)" : "");
+        }
+    });
+}
+```
+{% include [../steps/03_write_queries.md](../_includes/steps/03_write_queries.md) %}
+
+Фрагмент кода, демонстрирующий выполнение запроса на запись/изменение данных:
+
+```java
+private void upsertSimple() {
+    String query
+            = "UPSERT INTO episodes (series_id, season_id, episode_id, title) "
+            + "VALUES (2, 6, 1, \"TBD\");";
+
+    // Begin new transaction with SerializableRW mode
+    TxControl txControl = TxControl.serializableRw().setCommitTx(true);
+
+    // Executes data query with specified transaction control settings.
+    retryCtx.supplyResult(session -> session.executeDataQuery(query, txControl))
+        .join().expect("execute data query problem");
+}
+```
+
+{% include [steps/04_query_processing.md](../_includes/steps/04_query_processing.md) %}
+
+Для выполнения YQL-запросов используется метод `Session.executeDataQuery()`.
+SDK позволяет в явном виде контролировать выполнение транзакций и настраивать необходимый режим выполнения транзакций с помощью класса `TxControl`.
+
+В фрагменте кода, приведенного ниже, транзакция выполняется с помощью метода `session.executeDataQuery()`. Устанавливается режим выполнения транзакции `TxControl txControl = TxControl.serializableRw().setCommitTx(true);` и флаг автоматического завершения транзакции `setCommitTx(true)`. Тело запроса описано с помощью синтаксиса YQL и как параметр передается методу `executeDataQuery`.
+
+```java
+private void selectSimple() {
+    String query
+            = "SELECT series_id, title, release_date "
+            + "FROM series WHERE series_id = 1;";
+
+    // Begin new transaction with SerializableRW mode
+    TxControl txControl = TxControl.serializableRw().setCommitTx(true);
+
+    // Executes data query with specified transaction control settings.
+    DataQueryResult result = retryCtx.supplyResult(session -> session.executeDataQuery(query, txControl))
+            .join().expect("execute data query");
+
+    logger.info("--[ SelectSimple ]--");
+
+    ResultSetReader rs = result.getResultSet(0);
+    while (rs.next()) {
+        logger.info("read series with id {}, title {} and release_date {}",
+                rs.getColumn("series_id").getUint64(),
+                rs.getColumn("title").getUtf8(),
+                rs.getColumn("release_date").getDate()
+        );
+    }
+}
+```
+
+В результате исполнения запроса формируется объект класса `DataQueryResult`, который может содержать несколько выборок, получаемых методом `getResultSet( <index> )`. Так как запрос содержал только одну команду `SELECT`, то результат содержит только одну выборку под индексом `0`. Приведенный фрагмент кода при запуске выводит на консоль текст:
+
+```bash
+12:06:36.548 INFO  App - --[ SelectSimple ]--
+12:06:36.559 INFO  App - read series with id 1, title IT Crowd and release_date 2006-02-03
+```
+
+{% include [param_queries.md](../_includes/steps/06_param_queries.md) %}
+
+Фрагмент кода, приведенный ниже, демонстрирует использование параметризованных запросов и класс `Params` для формирования параметров и передачи их методу `executeDataQuery`.
+
+```java
+private void selectWithParams(long seriesID, long seasonID) {
+    String query
+            = "DECLARE $seriesId AS Uint64; "
+            + "DECLARE $seasonId AS Uint64; "
+            + "SELECT sa.title AS season_title, sr.title AS series_title "
+            + "FROM seasons AS sa INNER JOIN series AS sr ON sa.series_id = sr.series_id "
+            + "WHERE sa.series_id = $seriesId AND sa.season_id = $seasonId";
+
+    // Begin new transaction with SerializableRW mode
+    TxControl txControl = TxControl.serializableRw().setCommitTx(true);
+
+    // Type of parameter values should be exactly the same as in DECLARE statements.
+    Params params = Params.of(
+            "$seriesId", PrimitiveValue.uint64(seriesID),
+            "$seasonId", PrimitiveValue.uint64(seasonID)
+    );
+
+    DataQueryResult result = retryCtx.supplyResult(session -> session.executeDataQuery(query, txControl, params))
+            .join().expect("execute data query");
+
+    logger.info("--[ SelectWithParams ] -- ");
+
+    ResultSetReader rs = result.getResultSet(0);
+    while (rs.next()) {
+        logger.info("read season with title {} for series {}",
+                rs.getColumn("season_title").getUtf8(),
+                rs.getColumn("series_title").getUtf8()
+        );
+    }
+}
+```
+
+{% include [scan_query.md](../_includes/steps/08_scan_query.md) %}
+
+```java
+private void scanQueryWithParams(long seriesID, long seasonID) {
+    String query
+            = "DECLARE $seriesId AS Uint64; "
+            + "DECLARE $seasonId AS Uint64; "
+            + "SELECT ep.title AS episode_title, sa.title AS season_title, sr.title AS series_title "
+            + "FROM episodes AS ep "
+            + "JOIN seasons AS sa ON sa.season_id = ep.season_id "
+            + "JOIN series AS sr ON sr.series_id = sa.series_id "
+            + "WHERE sa.series_id = $seriesId AND sa.season_id = $seasonId;";
+
+    // Type of parameter values should be exactly the same as in DECLARE statements.
+    Params params = Params.of(
+            "$seriesId", PrimitiveValue.uint64(seriesID),
+            "$seasonId", PrimitiveValue.uint64(seasonID)
+    );
+
+    logger.info("--[ ExecuteScanQueryWithParams ]--");
+    retryCtx.supplyStatus(session -> {
+        ExecuteScanQuerySettings settings = ExecuteScanQuerySettings.newBuilder().build();
+        return session.executeScanQuery(query, params, settings, rs -> {
+            while (rs.next()) {
+                logger.info("read episode {} of {} for {}",
+                        rs.getColumn("episode_title").getUtf8(),
+                        rs.getColumn("season_title").getUtf8(),
+                        rs.getColumn("series_title").getUtf8()
+                );
+            }
+        });
+    }).join().expect("scan query problem");
+}
+```
+
+{% include [multistep_transactions.md](../_includes/steps/09_multistep_transactions.md) %}
+
+Для обеспечения корректности совместной работы транзакций и контекста ретраев - каждая транзация должна выполняться целиком внутри callback, передаваемого в `SessionRetryContext`. Возврат из callback должен происходить после полного завершения транзакции.
+
+Шаблон кода по использовании сложных транзакций в `SessionRetryContext`
+```java
+private void multiStepTransaction(long seriesID, long seasonID) {
+    retryCtx.supplyStatus(session -> {
+        // Multiple operations with session
+        ...
+
+        // return success status to SessionRetryContext
+        return CompletableFuture.completedFuture(Status.SUCCESS);
+    }).join().expect("multistep transaction problem");
+}
+
+```
+
+Первый шаг — подготовка и выполнение первого запроса:
+
+```java
+    String query1
+            = "DECLARE $seriesId AS Uint64; "
+            + "DECLARE $seasonId AS Uint64; "
+            + "SELECT MIN(first_aired) AS from_date FROM seasons "
+            + "WHERE series_id = $seriesId AND season_id = $seasonId;";
+
+    // Execute first query to get the required values to the client.
+    // Transaction control settings don't set CommitTx flag to keep transaction active
+    // after query execution.
+    TxControl tx1 = TxControl.serializableRw().setCommitTx(false);
+    DataQueryResult res1 = session.executeDataQuery(query1, tx1, Params.of(
+            "$seriesId", PrimitiveValue.uint64(seriesID),
+            "$seasonId", PrimitiveValue.uint64(seasonID)
+    )).join().expect("execute data query problem");
+```
+
+Затем мы можем выполнить некоторую клиентскую обработку полученных данных:
+
+```java
+    // Perform some client logic on returned values
+    ResultSetReader resultSet = res1.getResultSet(0);
+    if (!resultSet.next()) {
+        throw new RuntimeException("not found first_aired");
+    }
+    LocalDate fromDate = resultSet.getColumn("from_date").getDate();
+    LocalDate toDate = fromDate.plusDays(15);
+```
+
+И получить текущий `transaction id` для дальшейшей работы в рамках одной транзакции:
+
+```java
+    // Get active transaction id
+    String txId = res1.getTxId();
+```
+
+Следующий шаг — создание следующего запроса, использующего результаты выполнения кода на стороне клиентского приложения:
+
+```java
+    // Construct next query based on the results of client logic
+    String query2
+            = "DECLARE $seriesId AS Uint64;"
+            + "DECLARE $fromDate AS Date;"
+            + "DECLARE $toDate AS Date;"
+            + "SELECT season_id, episode_id, title, air_date FROM episodes "
+            + "WHERE series_id = $seriesId AND air_date >= $fromDate AND air_date <= $toDate;";
+
+    // Execute second query.
+    // Transaction control settings continues active transaction (tx) and
+    // commits it at the end of second query execution.
+    TxControl tx2 = TxControl.id(txId).setCommitTx(true);
+    DataQueryResult res2 = session.executeDataQuery(query2, tx2, Params.of(
+        "$seriesId", PrimitiveValue.uint64(seriesID),
+        "$fromDate", PrimitiveValue.date(fromDate),
+        "$toDate", PrimitiveValue.date(toDate)
+    )).join().expect("execute data query problem");
+
+    logger.info("--[ MultiStep ]--");
+    ResultSetReader rs = res2.getResultSet(0);
+    while (rs.next()) {
+        logger.info("read episode {} with air date {}",
+                rs.getColumn("title").getUtf8(),
+                rs.getColumn("air_date").getDate()
+        );
+    }
+```
+
+Приведенные фрагменты кода при запуске выводят на консоль текст:
+
+```bash
+12:06:36.850 INFO  App - --[ MultiStep ]--
+12:06:36.851 INFO  App - read episode Grow Fast or Die Slow with air date 2018-03-25
+12:06:36.851 INFO  App - read episode Reorientation with air date 2018-04-01
+12:06:36.851 INFO  App - read episode Chief Operating Officer with air date 2018-04-08
+```
+
+{% include [transaction_control.md](../_includes/steps/10_transaction_control.md) %}
+
+Фрагмент кода, демонстрирующий явное использование вызовов `beginTransaction()` и `transaction.Commit()`:
+
+```java
+private void tclTransaction() {
+    retryCtx.supplyStatus(session -> {
+        Transaction transaction = session.beginTransaction(TransactionMode.SERIALIZABLE_READ_WRITE)
+            .join().expect("begin transaction problem");
+
+        String query
+                = "DECLARE $airDate AS Date; "
+                + "UPDATE episodes SET air_date = $airDate WHERE title = \"TBD\";";
+
+        Params params = Params.of("$airDate", PrimitiveValue.date(Instant.now()));
+
+        // Execute data query.
+        // Transaction control settings continues active transaction (tx)
+        TxControl txControl = TxControl.id(transaction).setCommitTx(false);
+        DataQueryResult result = session.executeDataQuery(query, txControl, params)
+            .join().expect("execute date query problem");
+
+        logger.info("get transaction {}", result.getTxId());
+
+        // Commit active transaction (tx)
+        return transaction.commit();
+    }).join().expect("tcl transaction problem");
+}
+```
+
+

+ 1 - 1
ydb/docs/ru/core/reference/ydb-sdk/example/toc_i.yaml

@@ -8,7 +8,7 @@ items:
 - name: Go
   href: go/index.md
 - name: Java
-  href: example-java.md
+  href: java/index.md
 - name: Node.js
   href: example-nodejs.md
 - name: PHP

+ 7 - 0
ydb/docs/ru/core/reference/ydb-sdk/recipes/session_pool_limit/_includes/java.md

@@ -0,0 +1,7 @@
+```java
+this.tableClient = TableClient.newClient(rpc)
+        // 10 - minimum number of active sessions to keep in the pool during the cleanup
+        // 500 - maximum number of sessions in the pool
+        .sessionPoolSize(10, 500)
+        .build();
+```

+ 4 - 0
ydb/docs/ru/core/reference/ydb-sdk/recipes/session_pool_limit/index.md

@@ -17,5 +17,9 @@
 
   {% include [go.md](_includes/go.md) %}
 
+- Java
+
+
+  {% include [java.md](_includes/java.md) %}
 
 {% endlist %}