diff --git a/.github/workflows/jvm-compatibility.yml b/.github/workflows/jvm-compatibility.yml
index 5e4036494..26e5c64e7 100644
--- a/.github/workflows/jvm-compatibility.yml
+++ b/.github/workflows/jvm-compatibility.yml
@@ -150,7 +150,6 @@ jobs:
examples/client-v2-apache-arrow/pom.xml
examples/client-v2-json-processors/pom.xml
examples/demo-kotlin-service/pom.xml
- examples/demo-service/pom.xml
examples/jdbc-v2-json-processors/pom.xml
)
fi
diff --git a/README.md b/README.md
index a8dba8907..38a32dd53 100644
--- a/README.md
+++ b/README.md
@@ -88,7 +88,7 @@ Nightly Builds: https://s01.oss.sonatype.org/content/repositories/snapshots/com/
[Begin-with Usage Examples](../../tree/main/examples/client-v2)
-[Spring Demo Service](https://github.com/ClickHouse/clickhouse-java/tree/main/examples/demo-service)
+[Spring Demo Service](https://github.com/ClickHouse/clickhouse-java/tree/main/examples/demo-spring-service)
## JDBC Driver
diff --git a/docs/clickhouse-docs/client.mdx b/docs/clickhouse-docs/client.mdx
index 8b7185fc5..b408ed64a 100644
--- a/docs/clickhouse-docs/client.mdx
+++ b/docs/clickhouse-docs/client.mdx
@@ -825,7 +825,7 @@ This object should be closed as soon as possible to release a connection because
### Examples {#examples}
- Example code is available in [repo](https://github.com/ClickHouse/clickhouse-java/tree/main/examples/client-v2)
-- Reference Spring Service [implementation](https://github.com/ClickHouse/clickhouse-java/tree/main/examples/demo-service)
+- Reference Spring Service [implementation](https://github.com/ClickHouse/clickhouse-java/tree/main/examples/demo-spring-service)
## Common API {#common-api}
@@ -898,7 +898,7 @@ client.register(ArticleViewEvent.class, client.getTableSchema(TABLE_NAME));
Complete examples code is stored in the repo in a 'example` [folder](https://github.com/ClickHouse/clickhouse-java/tree/main/examples):
- [client-v2](https://github.com/ClickHouse/clickhouse-java/tree/main/examples/client-v2) - main set of examples.
-- [demo-service](https://github.com/ClickHouse/clickhouse-java/tree/main/examples/demo-service) - example of how to use the client in a Spring Boot application.
+- [demo-spring-service](https://github.com/ClickHouse/clickhouse-java/tree/main/examples/demo-spring-service) - example of how to use the client and the JDBC driver in a Spring Boot application, with observability.
- [demo-kotlin-service](https://github.com/ClickHouse/clickhouse-java/tree/main/examples/demo-kotlin-service) - example of how to use the client in Ktor (Kotlin) application.
## Reading Data {#reading-data}
diff --git a/examples/demo-service/.gitignore b/examples/demo-service/.gitignore
deleted file mode 100644
index 228daac5e..000000000
--- a/examples/demo-service/.gitignore
+++ /dev/null
@@ -1,32 +0,0 @@
-# Maven build output
-target/
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-bin/
-!**/src/main/**/bin/
-!**/src/test/**/bin/
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-out/
-!**/src/main/**/out/
-!**/src/test/**/out/
-
-### NetBeans ###
-/nbproject/private/
-/nbbuild/
-/dist/
-/nbdist/
-
-### VS Code ###
-.vscode/
diff --git a/examples/demo-service/README.md b/examples/demo-service/README.md
deleted file mode 100644
index 8c76c3191..000000000
--- a/examples/demo-service/README.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# ClickHouse-Java Demo Service
-
-## About
-This is an example of a Spring Boot service using ClickHouse client directly and via JPA.
-Example is an application that requires ClickHouse DB running externally. It can be a Docker or
-ClickHouse Cloud instance.
-
-## How to Run
-
-### Initialize DB
-Set up a ClickHouse instance.
-
-Example of running with Docker:
-```shell
-docker run -d --name demo-service-db -e CLICKHOUSE_USER=default -e CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 -e CLICKHOUSE_PASSWORD=secret\
- -p 8123:8123 clickhouse/clickhouse-server
-
-docker ps
-# output should be like:
-CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-2abfefc40a84 clickhouse/clickhouse-server "/entrypoint.sh" 4 seconds ago Up 2 seconds 9000/tcp, 0.0.0.0:8123->8123/tcp, :::8123->8123/tcp, 9009/tcp demo-service-db
-```
-
-Example how to run client:
-```shell
-docker exec -it demo-service-db clickhouse-client
-```
-
-Create table (needed for JPA example):
-```
-CREATE TABLE ui_events
-(
- `id` String,
- `timestamp` DateTime64,
- `event_name` String,
- `tags` Array(String)
-)
-ENGINE = MergeTree
-ORDER BY timestamp
-```
-
-### Run Demo-Service
-
-```shell
-mvn spring-boot:run
-```
-
-### Interact with API
-
-#### Direct Client
-
-To read `limit` number of rows from `system.numbers`:
-```shell
-curl http://localhost:8080/direct/dataset/0?limit=100000
-```
-
-#### JPA
-
-To insert some data:
-```shell
-curl -v -X POST -H "Content-Type: application/json" \
- -d '{"id": "4NAD7B8HH1", "timestamp": "2025-04-07T14:30:00.000Z", "eventName": "Login", "tags": ["security", "activity"]}'\
- http://localhost:8080/events/ui_events
-```
-
-To fetch inserted data:
-```shell
-curl -v -X GET http://localhost:8080/events/ui_events
-```
\ No newline at end of file
diff --git a/examples/demo-service/pom.xml b/examples/demo-service/pom.xml
deleted file mode 100644
index d96c1fa7f..000000000
--- a/examples/demo-service/pom.xml
+++ /dev/null
@@ -1,146 +0,0 @@
-
-
- 4.0.0
-
-
- org.springframework.boot
- spring-boot-starter-parent
- 3.4.4
-
-
-
- com.clickhouse
- demo-service
- 0.0.1-SNAPSHOT
- jar
-
- ClickHouse Java Demo Service
- Spring Boot service using the ClickHouse client directly and via JPA
- https://github.com/ClickHouse/clickhouse-java
- 2026
-
-
- ClickHouse, Inc.
- https://clickhouse.com/
-
-
-
-
- The Apache Software License, Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0
- repo
-
-
-
-
- https://github.com/ClickHouse/clickhouse-java
- scm:git@github.com:ClickHouse/clickhouse-java.git
- scm:git@github.com:ClickHouse/clickhouse-java.git
- HEAD
-
-
-
-
-
- ossrh
- Sonatype OSSRH
- https://central.sonatype.com/repository/maven-snapshots/
-
- false
-
-
- true
-
-
-
-
-
- 2026
- 17
-
- 0.12.0-rc1-SNAPSHOT
- all
- 1.18.42
- 1.14.3
-
-
-
-
- com.clickhouse
- clickhouse-jdbc
- ${clickhouse-java.version}
- ${clickhouse-packages.classifier}
-
-
-
- org.springframework.boot
- spring-boot-starter-actuator
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
-
-
-
- org.projectlombok
- lombok
- provided
-
-
-
- io.micrometer
- micrometer-core
- ${micrometer.version}
-
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
- org.junit.platform
- junit-platform-launcher
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
-
- org.projectlombok
- lombok
- ${lombok.version}
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- org.projectlombok
- lombok
-
-
-
-
-
-
-
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/BasicObjectsPool.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/BasicObjectsPool.java
deleted file mode 100644
index 6a1d94838..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/BasicObjectsPool.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.clickhouse.demo_service;
-
-import java.util.Deque;
-
-/**
- * Very basic object pool implementation.
- * It uses a deque to store objects. Leased objects are removed from the
- * deque and released objects are added back to the deque. When there is no objects in the deque,
- * a new object is created (assuming it will be returned on release).
- *
- * @param
- */
-public abstract class BasicObjectsPool {
-
- private Deque objects;
-
- BasicObjectsPool(Deque objects) {
- this(objects, 0);
- }
-
- BasicObjectsPool(Deque objects, int size) {
- this.objects = objects;
- for (int i = 0; i < size; i++) {
- this.objects.add(create());
- }
- }
-
- public T lease() {
- T obj = objects.poll();
- return obj != null ? obj : create();
- }
-
- public void release(T obj) {
- objects.addFirst(obj);
- }
-
- abstract T create();
-}
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/CalculationResult.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/CalculationResult.java
deleted file mode 100644
index f65bd9f3a..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/CalculationResult.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.clickhouse.demo_service;
-
-public class CalculationResult {
-
- long p1;
-
- public CalculationResult(long p1) {
- this.p1 = p1;
- }
-
- public long getP1() {
- return p1;
- }
-
- public void setP1(long p1) {
- this.p1 = p1;
- }
-}
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/DbConfiguration.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/DbConfiguration.java
deleted file mode 100644
index a81a7c535..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/DbConfiguration.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.clickhouse.demo_service;
-
-
-import com.clickhouse.client.api.Client;
-import io.micrometer.core.instrument.MeterRegistry;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
-import org.springframework.transaction.annotation.EnableTransactionManagement;
-
-@Configuration
-@EnableJpaRepositories
-@EnableTransactionManagement
-public class DbConfiguration {
-
- @Bean
- public Client chDirectClient(MeterRegistry meterRegistry, @Value("${db.url}") String dbUrl, @Value("${db.user}") String dbUser,
- @Value("${db.pass}") String dbPassword) {
- return new Client.Builder()
- .addEndpoint(dbUrl)
- .setUsername(dbUser)
- .setPassword(dbPassword)
-
- // sets the maximum number of connections to the server at a time
- // this is important for services handling many concurrent requests to ClickHouse
- .setMaxConnections(100)
- .setLZ4UncompressedBufferSize(1058576)
- .setSocketRcvbuf(500_000)
- .setSocketTcpNodelay(true)
- .setSocketSndbuf(500_000)
- .setClientNetworkBufferSize(500_000)
- .allowBinaryReaderToReuseBuffers(true) // using buffer pool for binary reader
- .registerClientMetrics(meterRegistry, "clickhouse-client-metrics")
- .build();
- }
-
- // JPA configured via application.properties
-}
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/DemoServiceApplication.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/DemoServiceApplication.java
deleted file mode 100644
index 13ee025f1..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/DemoServiceApplication.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.clickhouse.demo_service;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-@SpringBootApplication
-public class DemoServiceApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(DemoServiceApplication.class, args);
- }
-
-}
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/JPAInsertController.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/JPAInsertController.java
deleted file mode 100644
index 80c0012dc..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/JPAInsertController.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.clickhouse.demo_service;
-
-import com.clickhouse.demo_service.data.UIEvent;
-import com.clickhouse.demo_service.jpa.UIEventsDbRepository;
-import jakarta.annotation.PostConstruct;
-import jakarta.persistence.EntityManager;
-import jakarta.transaction.Transactional;
-import lombok.extern.java.Log;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.transaction.PlatformTransactionManager;
-import org.springframework.transaction.support.TransactionTemplate;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.Collection;
-
-/**
- * Class demonstrates usage of ClickHouse JDBC driver with JPA
- *
- */
-@RestController
-@RequestMapping("/events/")
-@Log
-public class JPAInsertController {
-
- @Autowired
- private UIEventsDbRepository uiEvents;
-
- @Autowired
- private PlatformTransactionManager transactionManager;
-
- @Autowired
- private EntityManager entityManager;
-
- private TransactionTemplate transactionTemplate;
-
- @PostConstruct
- public void setUp() {
- transactionTemplate = new TransactionTemplate(transactionManager);
- }
-
- @PostMapping("/ui_events")
- @Transactional(Transactional.TxType.NOT_SUPPORTED)
- public void addUIEvent(@RequestBody UIEvent event) {
- // do input validation and conversion
- transactionTemplate.executeWithoutResult(transactionStatus -> {
- entityManager.persist(event);
- });
- }
-
- @GetMapping("/ui_events")
- public Collection lastUIEvents() {
- return uiEvents.findAll();
- }
-}
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/MetricsConfig.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/MetricsConfig.java
deleted file mode 100644
index ba30ba589..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/MetricsConfig.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.clickhouse.demo_service;
-import io.micrometer.core.instrument.Clock;
-import io.micrometer.core.instrument.logging.LoggingMeterRegistry;
-import io.micrometer.core.instrument.logging.LoggingRegistryConfig;
-import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Condition;
-import org.springframework.context.annotation.Conditional;
-import org.springframework.context.annotation.Configuration;
-
-import java.time.Duration;
-
-@Configuration
-public class MetricsConfig {
-
- // Define a simple config for the logging registry
- @Bean
- public LoggingRegistryConfig loggingRegistryConfig() {
- return new LoggingRegistryConfig() {
- @Override
- public String get(String key) {
- // Only "step" is required. Returning null for others means defaults will be used.
- if ("step".equals(key)) {
- return "35s"; // Publish metrics every 5 seconds
- }
- return null;
- }
-
- @Override
- public Duration step() {
- return Duration.ofSeconds(5);
- }
- };
- }
-
- // Create the LoggingMeterRegistry bean.
- @Bean
- @ConditionalOnProperty("app.log_metrics")
- public LoggingMeterRegistry loggingMeterRegistry(LoggingRegistryConfig config) {
- LoggingMeterRegistry registry = new LoggingMeterRegistry(config, Clock.SYSTEM);
- // Start the registry’s internal scheduler so that metrics are published periodically.
- registry.start();
- return registry;
- }
-
- @Bean
- @ConditionalOnProperty(value = "app.log_metrics", havingValue = "false")
- public SimpleMeterRegistry simpleMeterRegistry() {
- SimpleMeterRegistry registry = new SimpleMeterRegistry();
-
- return registry;
- }
-}
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/ObjectsPreparedCollection.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/ObjectsPreparedCollection.java
deleted file mode 100644
index 1d0451cdb..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/ObjectsPreparedCollection.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.clickhouse.demo_service;
-
-import java.util.Deque;
-import java.util.Iterator;
-import java.util.function.Supplier;
-
-/**
- * This is a prepared collection of objects. It is aimed to solve the problem of
- * creating and releasing objects in a loop. After all objects are used, the collection
- * should be reset.
- *
- *
- * @param
- */
-public abstract class ObjectsPreparedCollection implements Supplier {
-
- private Deque objects;
-
- private Iterator iterator;
-
- ObjectsPreparedCollection(Deque objects) {
- this(objects, 0);
- }
-
- ObjectsPreparedCollection(Deque objects, int size) {
- this.objects = objects;
- for (int i = 0; i < size; i++) {
- this.objects.add(create());
- }
- this.reset();
- }
-
- @Override
- public T get() {
- T obj;
- if (iterator.hasNext()) {
- obj = iterator.next();
- } else {
- obj = create();
- objects.addFirst(obj);
- }
-
- return obj;
- }
-
- public void reset() {
- this.iterator = objects.iterator();
- }
- abstract T create();
-}
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/QueryController.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/QueryController.java
deleted file mode 100644
index 684c667a6..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/QueryController.java
+++ /dev/null
@@ -1,217 +0,0 @@
-package com.clickhouse.demo_service;
-
-
-import com.clickhouse.client.api.Client;
-import com.clickhouse.client.api.data_formats.ClickHouseBinaryFormatReader;
-import com.clickhouse.client.api.metadata.TableSchema;
-import com.clickhouse.client.api.metrics.ClientMetrics;
-import com.clickhouse.client.api.query.QueryResponse;
-import com.clickhouse.client.api.query.QuerySettings;
-import com.clickhouse.data.ClickHouseFormat;
-import com.clickhouse.demo_service.data.VirtualDatasetRecord;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.MappingIterator;
-import com.fasterxml.jackson.databind.json.JsonMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import jakarta.annotation.PostConstruct;
-import jakarta.servlet.http.HttpServletResponse;
-import lombok.extern.java.Log;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-/**
- * Class demonstrates using ClickHouse client directly from a service.
- * It avoids JDBC overhead and much easier to use.
- * Data may be streamed from database directly to the service response.
- */
-@RestController
-@RequestMapping("/dataset")
-@Log
-public class QueryController {
-
- private final Client chDirectClient;
-
- private static final int MAX_LIMIT = 100_000;
-
- private BasicObjectsPool> pool;
-
- public QueryController(Client chDirectClient) {
- this.chDirectClient = chDirectClient;
- }
-
- public TableSchema datasetQuerySchema;
-
- @PostConstruct
- public void setup() {
- chDirectClient.ping(3000); // helps to warm up the connection
-
- // Register class for deserialization
- datasetQuerySchema = chDirectClient.getTableSchemaFromQuery(DATASET_QUERY);
- chDirectClient.register(VirtualDatasetRecord.class, datasetQuerySchema);
- log.info("Dataset schema: " + datasetQuerySchema.getColumns());
-
- pool = new BasicObjectsPool<>(new ConcurrentLinkedDeque<>(), 100) {
- @Override
- ObjectsPreparedCollection create() {
- return new ObjectsPreparedCollection<>(new LinkedList<>(), MAX_LIMIT) {
- @Override
- VirtualDatasetRecord create() {
- return new VirtualDatasetRecord();
- }
- };
- }
- };
- }
-
- /**
- * Makes query to a {@code system.numbers} that can be used to generate a virtual dataset.
- * Size of the dataset is limited by the {@code limit} parameter.
- */
- private static final String DATASET_QUERY =
- "SELECT generateUUIDv4() as id, " +
- "toUInt32(number) as p1, " +
- "number, " +
- "toFloat32(number/100000) as p2, " +
- "toFloat64(number/100000) as p3" +
- " FROM system.numbers";
-
- /**
- * Fetches data from a DB using row binary reader. VirtualDatasetRecord objects are created on each iteration and
- * filled with data from the reader. Gives full control on how data is processed and stored.
- * If this method returns a lot of data it may cause application slowdown.
- *
- * @param limit
- * @return
- */
- @GetMapping("/reader")
- public List directDatasetFetch(@RequestParam(name = "limit", required = false) Integer limit) {
- limit = limit == null ? 100 : limit;
-
- final String query = DATASET_QUERY + " LIMIT " + limit;
- try (QueryResponse response = chDirectClient.query(query).get(3000, TimeUnit.MILLISECONDS)) {
- ArrayList result = new ArrayList<>();
-
- // iterable approach is more efficient for large datasets because it doesn't load all records into memory
- ClickHouseBinaryFormatReader reader = chDirectClient.newBinaryFormatReader(response);
-
- long start = System.nanoTime();
- while (reader.next() != null) {
- result.add(new VirtualDatasetRecord(
- reader.getUUID("id"),
- reader.getLong("p1"),
- reader.getBigInteger("number"),
- reader.getFloat("p2"),
- reader.getDouble("p3")
- ));
- }
- long duration = System.nanoTime() - start;
-
- // report metrics (only for demonstration purposes)
- log.info(String.format("records: %d, read time: %d ms (%d bytes), client time: %d ms, server time: %d ms",
- result.size(), TimeUnit.NANOSECONDS.toMillis(duration), response.getReadBytes(),
- response.getMetrics().getMetric(ClientMetrics.OP_DURATION).getLong(),
- TimeUnit.NANOSECONDS.toMillis(response.getServerTime())));
-
- return result.stream().findFirst().stream().collect(Collectors.toCollection(ArrayList::new));
- } catch (Exception e) {
- throw new RuntimeException("Failed to fetch dataset", e);
- }
- }
-
- private JsonMapper jsonMapper = new JsonMapper();
-
- /**
- * Reads data in JSONEachRow format, parses it into JSON library object (can be used for further processing) and
- * writes it back to the response.
- * This helps to reduce effort of writing data to the response.
- *
- * @param httpResp
- * @param limit
- */
- @GetMapping("/json_each_row_in_and_out")
- @ResponseBody
- public void directDataFetchJSONEachRow(HttpServletResponse httpResp, @RequestParam(name = "limit", required = false) Integer limit) {
- limit = limit == null ? 100 : limit;
-
- final String query = DATASET_QUERY + " LIMIT " + limit;
- QuerySettings settings = new QuerySettings().setFormat(ClickHouseFormat.JSONEachRow);
- try (QueryResponse response = chDirectClient.query(query, settings).get(3000, TimeUnit.MILLISECONDS);
- // JSONEachRow format is a stream of JSON objects, so we need to parse them one by one
- MappingIterator jsonIter = jsonMapper.readerFor(ObjectNode.class)
- .readValues(response.getInputStream())) {
- httpResp.setContentType("application/json");
- JsonGenerator jsonGen = jsonMapper.getFactory().createGenerator(httpResp.getOutputStream());
-
- jsonGen.writeStartArray();
- long start = System.nanoTime();
- int counter =0;
- while (jsonIter.hasNext()) {
- ObjectNode node = jsonIter.next();
- // here may be some processing logic
- node.put("ordNum", counter++);
-
- }
- jsonGen.writeEndArray();
- jsonGen.close();
- long duration = System.nanoTime() - start;
-
- // report metrics (only for demonstration purposes)
- log.info(String.format("records: %d, read time: %d ms, client time: %d ms, server time: %d ms",
- counter, TimeUnit.NANOSECONDS.toMillis(duration),
- response.getMetrics().getMetric(ClientMetrics.OP_DURATION).getLong(),
- TimeUnit.NANOSECONDS.toMillis(response.getServerTime())));
- } catch (Exception e) {
- throw new RuntimeException("Failed to fetch dataset", e);
- }
- }
-
- /**
- * Using POJO deserialization to fetch data from ClickHouse.
- * If cache is enabled, objects are reused from the pool otherwise new objects are created on each iteration.
- *
- * @param limit
- * @return
- */
- @GetMapping("/read_to_pojo")
- public CalculationResult directDatasetReadToPojo(@RequestParam(name = "limit", required = false) Integer limit,
- @RequestParam(name = "cache", required = false) Boolean cache) {
- limit = limit == null ? 100 : limit;
- cache = cache != null && cache;
- return readToPOJO(limit, cache);
- }
-
- private CalculationResult readToPOJO(int limit, boolean cache) {
- final String query = DATASET_QUERY + " LIMIT " + limit;
- List result = null;
- Supplier objectsPool = cache ? this.pool.lease()
- : VirtualDatasetRecord::new;
- try {
- long start = System.nanoTime();
-
- result = chDirectClient.queryAll(query, VirtualDatasetRecord.class, datasetQuerySchema, objectsPool);
- long duration = System.nanoTime() - start;
- log.info("records: " + result.size() + ", read time: " + TimeUnit.NANOSECONDS.toMillis(duration) + " ms");
- long p1Sum = 0;
- for (VirtualDatasetRecord record : result) {
- p1Sum += record.getP1();
- }
- if (cache) {
- ((ObjectsPreparedCollection) objectsPool).reset();
- }
- return new CalculationResult(p1Sum);
- } catch (Exception e) {
- throw new RuntimeException("Failed to fetch dataset", e);
- } finally {
- if (cache) {
- this.pool.release((ObjectsPreparedCollection) objectsPool);
- }
- }
- }
-}
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/data/UIEvent.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/data/UIEvent.java
deleted file mode 100644
index 0a2403fcf..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/data/UIEvent.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.clickhouse.demo_service.data;
-
-import com.clickhouse.demo_service.jpa.ClickHouseStringArrayType;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Id;
-import jakarta.persistence.Table;
-import lombok.Data;
-import org.hibernate.annotations.Array;
-import org.hibernate.annotations.JdbcType;
-import org.hibernate.annotations.Type;
-import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
-
-import java.sql.JDBCType;
-import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.List;
-
-@Entity
-@Data
-@Table(name = "ui_events")
-public class UIEvent {
-
- @Id
- private String id;
-
- private Timestamp timestamp;
-
- private String eventName;
-
- @JdbcType(ArrayJdbcType.class)
- @Type(ClickHouseStringArrayType.class)
- private Collection tags;
-}
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/data/VirtualDatasetRecord.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/data/VirtualDatasetRecord.java
deleted file mode 100644
index 1c75e39bf..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/data/VirtualDatasetRecord.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.clickhouse.demo_service.data;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.ToString;
-
-import java.math.BigInteger;
-import java.util.UUID;
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-@ToString
-public class VirtualDatasetRecord {
-
- private UUID id;
-
- private long p1;
-
- private BigInteger number;
-
- private float p2;
-
- private double p3;
-}
diff --git a/examples/demo-service/src/main/java/com/clickhouse/demo_service/jpa/ClickHouseStringArrayType.java b/examples/demo-service/src/main/java/com/clickhouse/demo_service/jpa/ClickHouseStringArrayType.java
deleted file mode 100644
index 1f2b1f61a..000000000
--- a/examples/demo-service/src/main/java/com/clickhouse/demo_service/jpa/ClickHouseStringArrayType.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.clickhouse.demo_service.jpa;
-
-import org.hibernate.engine.spi.SharedSessionContractImplementor;
-import org.hibernate.usertype.UserType;
-
-import java.io.Serializable;
-import java.sql.*;
-import java.util.*;
-
-public class ClickHouseStringArrayType implements UserType {
- @Override
- public int getSqlType() {
- return Types.ARRAY;
- }
-
- @Override
- public Class returnedClass() {
- return Collection.class;
- }
-
- @Override
- public boolean equals(Collection x, Collection y) {
- return x.equals(y);
- }
-
- @Override
- public int hashCode(Collection x) {
- return x.hashCode();
- }
-
- @Override
- public Collection nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) throws SQLException {
- // This implementation not optimal, but portable.
- Array array = rs.getArray(position);
- if (array != null) {
- List