Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

عميل Java

مكتبة Java client للتواصل مع خادم DB عبر بروتوكولاته. يدعم التطبيق الحالي واجهة HTTP interface فحسب. توفر المكتبة واجهة برمجة تطبيقات خاصة بها لإرسال الطلبات إلى الخادم، كما توفر أدوات للعمل مع تنسيقات البيانات الثنائية المختلفة (RowBinary* & Native*).

الإعداد


<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>client-v2</artifactId>
    <version>0.9.8</version>
</dependency>

التهيئة

يتم تهيئة كائن Client بواسطة com.clickhouse.client.api.Client.Builder#build(). يمتلك كل عميل سياقه الخاص ولا تُشارَك أي كائنات بينهما. يوفر الـ Builder طرق تكوين لتسهيل الإعداد.

مثال:

 Client client = new Client.Builder()
                .addEndpoint("https://clickhouse-cloud-instance:8443/")
                .setUsername(user)
                .setPassword(password)
                .build();

Client هو AutoCloseable ويجب إغلاقه عند انتهاء الحاجة إليه.

المصادقة

تُهيَّأ المصادقة لكل عميل في مرحلة التهيئة. وتتوفر ثلاث طرق مصادقة مدعومة: بكلمة المرور، أو برمز الوصول، أو بشهادة SSL للعميل.

تتطلب المصادقة بكلمة المرور تعيين اسم المستخدم وكلمة المرور عبر استدعاء setUsername(String) وsetPassword(String):

 Client client = new Client.Builder()
        .addEndpoint("https://clickhouse-cloud-instance:8443/")
        .setUsername(user)
        .setPassword(password)
        .build();

تتطلب المصادقة عبر رمز الوصول ضبط رمز الوصول من خلال استدعاء setAccessToken(String):

 Client client = new Client.Builder()
        .addEndpoint("https://clickhouse-cloud-instance:8443/")
        .setAccessToken(userAccessToken)
        .build();

تتطلب المصادقة عبر شهادة SSL للعميل تحديد اسم المستخدم، وتفعيل مصادقة SSL، وضبط شهادة العميل ومفتاحه من خلال استدعاء setUsername(String) وuseSSLAuthentication(boolean) وsetClientCertificate(String) وsetClientKey(String) على التوالي:

Client client = new Client.Builder()
        .useSSLAuthentication(true)
        .setUsername("some_user")
        .setClientCertificate("some_user.crt")
        .setClientKey("some_user.key")

التهيئة

يتم تعريف جميع الإعدادات بواسطة طرق المثيل (المعروفة أيضاً بطرق التهيئة)، مما يوضح نطاق وسياق كل قيمة. تُعرَّف معاملات التهيئة الرئيسية في نطاق واحد (العميل أو العملية) ولا تتجاوز بعضها البعض.

يتم تحديد الإعداد أثناء إنشاء الـ client. راجع com.clickhouse.client.api.Client.Builder.

إعداد العميل

الطريقة الوسائط الوصف الافتراضي المفتاح
addEndpoint(String endpoint) endpoint - عنوان الخادم بتنسيق URL يضيف نقطة نهاية خادم إلى قائمة الخوادم المتاحة. حاليًا، لا يتم دعم سوى نقطة نهاية واحدة. none none
addEndpoint(Protocol protocol, String host, int port, boolean secure) protocol - بروتوكول الاتصال
host - عنوان IP أو اسم المضيف
secure - استخدام HTTPS
يضيف نقطة نهاية خادم إلى قائمة الخوادم المتاحة. حاليًا، لا يتم دعم سوى نقطة نهاية واحدة. none none
enableConnectionPool(boolean enable) enable - راية للتفعيل/التعطيل يحدد ما إذا كان مجمّع الاتصالات مفعّلًا true connection_pool_enabled
setMaxConnections(int maxConnections) maxConnections - عدد الاتصالات يحدد عدد الاتصالات التي يمكن للعميل فتحها لكل نقطة نهاية خادم. 10 max_open_connections
setConnectionTTL(long timeout, ChronoUnit unit) timeout - قيمة المهلة
unit - وحدة الزمن
يحدد قيمة TTL للاتصال، وبعدها سيُعتبر الاتصال غير نشط -1 connection_ttl
setKeepAliveTimeout(long timeout, ChronoUnit unit) timeout - قيمة المهلة
unit - وحدة الزمن
يحدد مهلة Keep-Alive لاتصال HTTP. اضبطها على 0 لتعطيل Keep-Alive. - http_keep_alive_timeout
setConnectionReuseStrategy(ConnectionReuseStrategy strategy) strategy - LIFO أو FIFO يحدد الاستراتيجية التي يجب أن يستخدمها مجمّع الاتصالات FIFO connection_reuse_strategy
setDefaultDatabase(String database) database - اسم قاعدة بيانات يحدد قاعدة البيانات الافتراضية. default database

تعريف العميل

يوجد حقلان في سجل الاستعلام يُحدِّدان التطبيق الذي أنشأ الطلب: client_name وhttp_user_agent. يستخدم بروتوكول native TCP الحقلَ client_name لتعريف التطبيق، بينما يستخدم بروتوكول HTTP الحقلَ http_user_agent لهذا الغرض. يوفر الـ Client builder الأسلوبَ setClientName لضبط القيم الصحيحة لكلا البروتوكولين. يُضبط الحقل http_user_agent وفق التنسيق الشائع لترويسة User-Agent: application-name[/version] [(operating-system; architecture; ...)]. تتكرر هذه المجموعة من القيم لكل طبقة: التطبيق، ومكتبة الـ client، ومكتبة الـ HTTP client. ويأتي ما يُحدَّد عبر الأسلوب setClientName في مقدمة القائمة.

على سبيل المثال:

client.setClientName("my-app-01/1.0");

سيؤدي ذلك إلى القيمة التالية لـ http_user_agent:

my-app-01/1.0 clickhouse-java-v2/0.9.6-SNAPSHOT (Linux; jvm:17.0.17) Apache-HttpClient/5.4.4

يمكن للتطبيق تعيين رأس HTTP الخاص به User-Agent للتعريف عن نفسه. غير أن الجزء clickhouse-java-v2/0.9.6-SNAPSHOT سيُلحَق بنهاية الرأس.

تحديد العملية

يتضمن سجل الاستعلام حقلين إضافيين هما query_id وlog_comment، يمكن استخدامهما لتحديد عملية معينة وإضافة معلومات إضافية إلى سجل الاستعلام.

query_id هو معرف فريد لعملية. يمكن تعيينه بواسطة التطبيق عن طريق استدعاء الدالة setQueryId في فئة QuerySettings.

QuerySettings querySettings = new QuerySettings();
querySettings.setQueryId("some-query-id");

log_comment هو تعليق يمكن إضافته إلى سجل الاستعلام. يمكن للتطبيق ضبطه عبر استدعاء الدالة logComment من الفئة QuerySettings.

QuerySettings querySettings = new QuerySettings();
querySettings.logComment("some-comment");

إعدادات الخادم

يمكن تعيين إعدادات جانب الخادم على مستوى العميل مرةً واحدة أثناء الإنشاء (راجع الدالة serverSetting في Builder)، وعلى مستوى العملية (راجع serverSetting في class إعدادات العملية).

 try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost", mockServer.port(), false)
        .setUsername("default")
        .setPassword(ClickHouseServerForTest.getPassword())
        .compressClientRequest(true)

        // Client level
        .serverSetting("max_threads", "10")
        .serverSetting("async_insert", "1")
        .serverSetting("roles", Arrays.asList("role1", "role2"))

        .build()) {

	// Operation level
	QuerySettings querySettings = new QuerySettings();
	querySettings.serverSetting("session_timezone", "Europe/Zurich");

	...
}

⚠️ عند تعيين الخيارات عبر طريقة setOption (سواء في Client.Builder أو في فئة إعدادات العملية)، يجب أن يُسبَق اسم إعداد الخادم بالبادئة clickhouse_setting_. قد يكون com.clickhouse.client.api.ClientConfigProperties#serverSetting() مفيدًا في هذه الحالة.

ترويسة HTTP مخصصة

يمكن تعيين HTTP headers مخصصة لجميع العمليات (على مستوى العميل) أو لعملية واحدة فقط (على مستوى العملية).


QuerySettings settings = new QuerySettings()
    .httpHeader(HttpHeaders.REFERER, clientReferer)
    .setQueryId(qId);

عند تعيين الخيارات عبر الـ method الخاصة بـ setOption (سواء في Client.Builder أو class إعدادات العملية)، يجب أن يبدأ اسم الـ header المخصص بالبادئة http_header_. وقد تكون الـ method الخاصة بـ com.clickhouse.client.api.ClientConfigProperties#httpHeader() مفيدةً في هذه الحالة.

التعريفات الشائعة

ClickHouseFormat

تعداد الصيغ المدعومة. يتضمن جميع الصيغ التي يدعمها ClickHouse.

  • raw - يجب على المستخدم تحويل ترميز البيانات الخام
  • full - يمكن للعميل تحويل ترميز البيانات بنفسه ويقبل تدفق بيانات خام
  • - - هذه العملية غير مدعومة في ClickHouse لهذا التنسيق

يدعم إصدار client هذا:

الصيغة الإدخال الإخراج
TabSeparated raw raw
TabSeparatedRaw raw raw
TabSeparatedWithNames raw raw
TabSeparatedWithNamesAndTypes raw raw
TabSeparatedRawWithNames raw raw
TabSeparatedRawWithNamesAndTypes raw raw
Template raw raw
TemplateIgnoreSpaces raw -
CSV raw raw
CSVWithNames raw raw
CSVWithNamesAndTypes raw raw
CustomSeparated raw raw
CustomSeparatedWithNames raw raw
CustomSeparatedWithNamesAndTypes raw raw
SQLInsert - raw
Values raw raw
Vertical - raw
JSON raw raw
JSONAsString raw -
JSONAsObject raw -
JSONStrings raw raw
JSONColumns raw raw
JSONColumnsWithMetadata raw raw
JSONCompact raw raw
JSONCompactStrings - raw
JSONCompactColumns raw raw
JSONEachRow raw raw
PrettyJSONEachRow - raw
JSONEachRowWithProgress - raw
JSONStringsEachRow raw raw
JSONStringsEachRowWithProgress - raw
JSONCompactEachRow raw raw
JSONCompactEachRowWithNames raw raw
JSONCompactEachRowWithNamesAndTypes raw raw
JSONCompactStringsEachRow raw raw
JSONCompactStringsEachRowWithNames raw raw
JSONCompactStringsEachRowWithNamesAndTypes raw raw
JSONObjectEachRow raw raw
BSONEachRow raw raw
TSKV raw raw
Pretty - raw
PrettyNoEscapes - raw
PrettyMonoBlock - raw
PrettyNoEscapesMonoBlock - raw
PrettyCompact - raw
PrettyCompactNoEscapes - raw
PrettyCompactMonoBlock - raw
PrettyCompactNoEscapesMonoBlock - raw
PrettySpace - raw
PrettySpaceNoEscapes - raw
PrettySpaceMonoBlock - raw
PrettySpaceNoEscapesMonoBlock - raw
Prometheus - raw
Protobuf raw raw
ProtobufSingle raw raw
ProtobufList raw raw
Avro raw raw
AvroConfluent raw -
Parquet raw raw
ParquetMetadata raw -
Arrow خام خام
ArrowStream خام خام
ORC خام خام
One خام -
Npy خام خام
RowBinary كامل كامل
RowBinaryWithNames كامل كامل
RowBinaryWithNamesAndTypes كامل كامل
RowBinaryWithDefaults كامل -
Native كامل خام
Null - خام
XML - خام
CapnProto خام خام
LineAsString خام خام
Regexp خام -
RawBLOB خام خام
MsgPack خام خام
MySQLDump خام -
DWARF خام -
Markdown - خام
Form خام -

واجهة برمجة تطبيقات الإدراج

insert(String tableName, InputStream data, ClickHouseFormat format)

يقبل البيانات على شكل InputStream من البايتات بالتنسيق المحدد. ومن المتوقع أن تكون data مُرمَّزةً وفق format.

التوقيعات

CompletableFuture<InsertResponse> insert(String tableName, InputStream data, ClickHouseFormat format, InsertSettings settings)
CompletableFuture<InsertResponse> insert(String tableName, InputStream data, ClickHouseFormat format)

المعاملات

tableName - اسم الجدول الهدف.

data - تدفق إدخال لبيانات مُرمَّزة.

format - التنسيق الذي تُرمَّز به البيانات.

settings - إعدادات الطلب.

القيمة المُعادة

مستقبل من نوع InsertResponse - نتيجة العملية ومعلومات إضافية مثل مقاييس جانب الخادم.

أمثلة

try (InputStream dataStream = getDataStream()) {
    try (InsertResponse response = client.insert(TABLE_NAME, dataStream, ClickHouseFormat.JSONEachRow,
            insertSettings).get(3, TimeUnit.SECONDS)) {

        log.info("Insert finished: {} rows written", response.getMetrics().getMetric(ServerMetrics.NUM_ROWS_WRITTEN).getLong());
    } catch (Exception e) {
        log.error("Failed to write JSONEachRow data", e);
        throw new RuntimeException(e);
    }
}

insert(String tableName, List<?> data, InsertSettings settings)

يُرسل طلب كتابة إلى قاعدة البيانات. يتم تحويل قائمة الكائنات إلى تنسيق فعّال ثم إرسالها إلى الخادم. يجب تسجيل الفئة الخاصة بعناصر القائمة مسبقًا باستخدام الطريقة register(Class, TableSchema).

التوقيعات

client.insert(String tableName, List<?> data, InsertSettings settings)
client.insert(String tableName, List<?> data)

المعاملات

tableName - اسم الجدول الهدف.

data - كائنات DTO (Data Transfer Object) الخاصة بالمجموعة.

settings - إعدادات الطلب.

القيمة المُعادة

مستقبل من النوع InsertResponse - يمثّل نتيجة العملية ومعلومات إضافية كمقاييس جانب الخادم.

أمثلة

// Important step (done once) - register class to pre-compile object serializer according to the table schema.
client.register(ArticleViewEvent.class, client.getTableSchema(TABLE_NAME));

List<ArticleViewEvent> events = loadBatch();

try (InsertResponse response = client.insert(TABLE_NAME, events).get()) {
    // handle response, then it will be closed and connection that served request will be released.
}

InsertSettings

خيارات الإعداد لعمليات الإدراج.

طرق الإعداد

الطريقة الوصف
setQueryId(String queryId) يضبط معرّف الاستعلام الذي سيُعيَّن للعملية. القيمة الافتراضية: null.
setDeduplicationToken(String token) يعيّن رمز إزالة التكرار. يُرسَل هذا الرمز إلى الخادم، ويمكن استخدامه لتحديد الاستعلام. القيمة الافتراضية: null.
setInputStreamCopyBufferSize(int size) حجم مخزن النسخ المؤقت. يُستخدم هذا المخزن المؤقت أثناء عمليات الكتابة لنسخ البيانات من دفق إدخال يوفّره المستخدم إلى دفق إخراج. القيمة الافتراضية: 8196.
serverSetting(String name, String value) يضبط إعدادات خادم فردية لعملية معيّنة.
serverSetting(String name, Collection values) يُعيّن إعدادات خادم معيّنة ذات قيم متعددة لعملية ما. يجب أن تكون عناصر المجموعة من النوع String.
setDBRoles(Collection dbRoles) يعيّن أدوار قاعدة البيانات المراد تعيينها قبل تنفيذ العملية. يجب أن تكون عناصر المجموعة قيماً من نوع String.
setOption(String option, Object value) يضبط خيار تهيئة بتنسيقه الخام. وهذا ليس إعداد خادم.

InsertResponse

كائن Response يحتوي على نتيجة عملية الإدراج. لا يكون متاحاً إلا إذا تلقّى الـ Client استجابةً من الـ server.

الطريقة الوصف
OperationMetrics getMetrics() يعيد كائنًا يتضمن مقاييس العملية.
String getQueryId() يعيد معرّف الاستعلام المخصَّص للعملية من قِبل التطبيق (عبر إعدادات العملية) أو من قِبل الخادم.

واجهة برمجة تطبيقات الاستعلام

query(String sqlQuery)

يُرسل sqlQuery كما هو. يُحدَّد تنسيق الاستجابة من خلال إعدادات الاستعلام. سيحتفظ QueryResponse بمرجع إلى تدفق الاستجابة الذي يجب أن يستهلكه قارئ يدعم التنسيق المحدد.

التوقيعات

CompletableFuture<QueryResponse> query(String sqlQuery, QuerySettings settings)
CompletableFuture<QueryResponse> query(String sqlQuery)

المعاملات

sqlQuery - عبارة SQL واحدة. يُرسَل الاستعلام كما هو إلى الخادم.

settings - إعدادات الطلب.

القيمة المُعادة

مستقبل من النوع QueryResponse - مجموعة بيانات نتائج ومعلومات إضافية كمقاييس جانب الخادم. يجب إغلاق كائن الاستجابة بعد الانتهاء من استهلاك مجموعة البيانات.

أمثلة

final String sql = "select * from " + TABLE_NAME + " where title <> '' limit 10";

// Default format is RowBinaryWithNamesAndTypesFormatReader so reader have all information about columns
try (QueryResponse response = client.query(sql).get(3, TimeUnit.SECONDS);) {

    // Create a reader to access the data in a convenient way
    ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);

    while (reader.hasNext()) {
        reader.next(); // Read the next record from stream and parse it

        // get values
        double id = reader.getDouble("id");
        String title = reader.getString("title");
        String url = reader.getString("url");

        // collecting data
    }
} catch (Exception e) {
    log.error("Failed to read data", e);
}

// put business logic outside of the reading block to release http connection asap.

query(String sqlQuery, Map<String, Object> queryParams, QuerySettings settings)

يُرسل sqlQuery كما هو. كما يُرسل معاملات الاستعلام لكي يتمكن الخادم من تجميع تعبير SQL.

التوقيعات

CompletableFuture<QueryResponse> query(String sqlQuery, Map<String, Object> queryParams, QuerySettings settings)

المعاملات

sqlQuery - تعبير SQL مع العناصر النائبة {}.

queryParams - خريطة من المتغيرات لإكمال تعبير SQL على الخادم.

settings - إعدادات الطلب.

القيمة المُعادة

مستقبل من النوع QueryResponse - مجموعة بيانات نتائج ومعلومات إضافية كمقاييس جانب الخادم. يجب إغلاق كائن الاستجابة بعد الانتهاء من استهلاك مجموعة البيانات.

أمثلة


// define parameters. They will be sent to the server along with the request.
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("param1", 2);

try (QueryResponse response =
        client.query("SELECT * FROM " + table + " WHERE col1 >= {param1:UInt32}", queryParams, new QuerySettings()).get()) {

    // Create a reader to access the data in a convenient way
    ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);

    while (reader.hasNext()) {
        reader.next(); // Read the next record from stream and parse it

        // reading data
    }

} catch (Exception e) {
    log.error("Failed to read data", e);
}

queryAll(String sqlQuery)

يُرسل استعلامات للبيانات بتنسيق RowBinaryWithNamesAndTypes. يُعيد النتيجة على شكل مجموعة. أداء القراءة مماثل لأداء القارئ، إلا أنه يتطلب ذاكرة أكبر لاستيعاب مجموعة البيانات بالكامل.

التوقيعات

List<GenericRecord> queryAll(String sqlQuery)

المعلمات

sqlQuery - تعبير SQL للاستعلام عن البيانات من الخادم.

القيمة المُعادة

مجموعة البيانات الكاملة ممثَّلةً بقائمة من كائنات GenericRecord التي توفر وصولاً على شكل صفوف إلى بيانات النتائج.

أمثلة

try {
    log.info("Reading whole table and process record by record");
    final String sql = "select * from " + TABLE_NAME + " where title <> ''";

    // Read whole result set and process it record by record
    client.queryAll(sql).forEach(row -> {
        double id = row.getDouble("id");
        String title = row.getString("title");
        String url = row.getString("url");

        log.info("id: {}, title: {}, url: {}", id, title, url);
    });
} catch (Exception e) {
    log.error("Failed to read data", e);
}

QuerySettings

خيارات التهيئة لعمليات الاستعلام.

طرق الإعداد

الأسلوب الوصف
setQueryId(String queryId) يعيّن معرّف الاستعلام الذي سيُسنَد إلى العملية.
setFormat(ClickHouseFormat format) يضبط تنسيق الاستجابة. راجع RowBinaryWithNamesAndTypes للاطلاع على القائمة الكاملة.
setMaxExecutionTime(Integer maxExecutionTime) يعيّن وقت تنفيذ العملية على الخادم. لا يؤثر ذلك في مهلة القراءة.
waitEndOfQuery(Boolean waitEndOfQuery) يطلب من الخادم انتظار اكتمال الاستعلام قبل إرسال الاستجابة.
setUseServerTimeZone(Boolean useServerTimeZone) ستُستخدم المنطقة الزمنية للخادم (راجع إعدادات العميل) لتحليل أنواع التاريخ/الوقت في ناتج العملية. القيمة الافتراضية false.
setUseTimeZone(String timeZone) يطلب من الخادم استخدام timeZone لتحويل الوقت. راجع session_timezone.
serverSetting(String name, String value) يعيّن إعداد خادم محددًا للعملية.
serverSetting(String name, Collection values) يُعيّن إعدادات خادم فردية بقيم متعددة لعملية ما. يجب أن تكون عناصر المجموعة من النوع String.
setDBRoles(Collection dbRoles) يعيّن أدوار قاعدة البيانات المراد تعيينها قبل تنفيذ العملية. يجب أن تكون عناصر المجموعة قيماً من نوع String.
setOption(String option, Object value) يضبط خيار تهيئة بتنسيق خام. هذا ليس إعدادًا على مستوى الخادم.

QueryResponse

كائن الاستجابة الذي يحتوي على نتيجة تنفيذ الاستعلام. لا يكون متاحاً إلا إذا تلقّى الـ client استجابةً من الـ server.

الطريقة الوصف
ClickHouseFormat getFormat() يعيد التنسيق الذي تُرمَّز به البيانات في الاستجابة.
InputStream getInputStream() يعيد تدفق بايتات البيانات غير المضوطة بالتنسيق المحدد.
OperationMetrics getMetrics() يعيد كائنًا يحتوي على مقاييس العملية.
String getQueryId() يعيد معرّف الاستعلام الذي عيّنه التطبيق للعملية (عبر إعدادات العملية أو بواسطة الخادم).
TimeZone getTimeZone() يعيد المنطقة الزمنية التي يجب استخدامها لمعالجة النوعين Date وDateTime في الاستجابة.

أمثلة

واجهة برمجة التطبيقات المشتركة

getTableSchema(String table)

يجلب مخطط الجدول لـ table.

التوقيعات

TableSchema getTableSchema(String table)
TableSchema getTableSchema(String table, String database)

المعاملات

table - اسم الجدول الذي سيتم جلب بيانات المخطط الخاصة به.

database - قاعدة البيانات التي يُعرَّف فيها الجدول الهدف.

القيمة المُعادة

يُعيد كائن TableSchema يحتوي على قائمة بأعمدة الجدول.

getTableSchemaFromQuery(String sql)

يسترجع المخطط من عبارة SQL.

التوقيعات

TableSchema getTableSchemaFromQuery(String sql)

المعاملات

sql - عبارة SQL "SELECT" التي يجب إرجاع مخططها.

القيمة المُعادة

يُعيد كائن TableSchema يحتوي على أعمدة تتطابق مع تعبير sql.

TableSchema

register(Class<?> clazz, TableSchema schema)

يُجمِّع طبقة التسلسل وإلغاء التسلسل لـ Java Class لاستخدامها في كتابة/قراءة البيانات باستخدام schema. ستُنشئ هذه الدالة مُسلسِلاً ومُفككَ تسلسل لزوج getter/setter والعمود المقابل. يُحدَّد تطابق العمود باستخراج اسمه من اسم الدالة. على سبيل المثال، سيرتبط getFirstName بالعمود first_name أو firstname.

التوقيعات

void register(Class<?> clazz, TableSchema schema)

المعاملات

clazz - فئة تمثّل POJO المستخدم لقراءة البيانات وكتابتها.

schema - مخطط البيانات المستخدم للمطابقة مع خصائص POJO.

أمثلة

client.register(ArticleViewEvent.class, client.getTableSchema(TABLE_NAME));

أمثلة الاستخدام

تجد شيفرة الأمثلة الكاملة في المستودع داخل مجلد 'example`:

  • client-v2 - المجموعة الأساسية من الأمثلة.
  • demo-service - مثال على كيفية استخدام العميل في تطبيق Spring Boot.
  • demo-kotlin-service - مثال على كيفية استخدام العميل في تطبيق Ktor ‏(Kotlin).

قراءة البيانات

ثمة طريقتان شائعتان لقراءة البيانات:

  • الطريقة query() التي تُرجع الكائن منخفض المستوى QueryResponse، والذي يحتوي على InputStream للبيانات. تُستخدم عادةً مع ClickHouseBinaryFormatReader لإجراء عمليات قراءة متدفقة، لكنها يمكن استخدامها أيضًا مع أي تنفيذ مخصص آخر للقارئ. كما يوفّر QueryResponse إمكانية الوصول إلى البيانات الوصفية لمجموعة النتائج والمقاييس.
  • الطريقة queryAll() مع استخدام GenericRecord للوصول بسهولة إلى الصفوف. في هذه الحالة، تُحمَّل مجموعة النتائج بالكامل إلى الذاكرة.
  • الطريقة queryRecords() التي تُرجع com.clickhouse.client.api.query.Records - وهو مكرّر لكائنات GenericRecord. تستخدم هذه الطريقة نهجًا متدفقًا (من دون تحميل أي بيانات إلى الذاكرة) وتستفيد من GenericRecord للوصول إلى البيانات.

ملاحظة: يستلزم أسلوب البث قراءةً سريعة، وإلا قد يتسبب في انتهاء مهلة الكتابة على الخادم، إذ تُقرأ البيانات مباشرةً من تدفق الشبكة.

قراءة المصفوفات

طرق ClickHouseBinaryFormatReader

  • getList(...) - يقرأ أي Array(...) باعتباره List<T>. خيار افتراضي جيد لعمليات القراءة المرنة المقيّدة بالنوع. يدعم المصفوفات المتداخلة.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - الأفضل للمصفوفات أحادية البعد ذات القيم المتوافقة مع الأنواع الأولية.
  • getStringArray(...) - مخصّص لـ Array(String) (وكذلك لقيم enum الممثلة كأسماء).
  • getObjectArray(...) - خيار عام لأي نوع عنصر في Array(...)، بما في ذلك المصفوفات المتداخلة. استخدمه لقراءة المصفوفات التي تحتوي على قيم يمكن أن تكون NULL والمصفوفات المتداخلة.

تتوفر التحميلات الزائدة المستندة إلى الفهرس والمستندة إلى الاسم لجميع الطرق. يبدأ الفهرس من 1. تتيح الطرق المستندة إلى الفهرس الوصول المباشر إلى العمود. تتطلب الطرق المستندة إلى الاسم البحث عن الفهرس في كل مرة.

try (QueryResponse response = client.query("SELECT * FROM my_table").get()) {
    ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);
    while (reader.next() != null) {

        Object[] uint64 = reader.getObjectArray("uint64_arr"); // Array(UInt64) -> BigInteger[]
        Object[] arr2d = reader.getObjectArray("arr2d");       // Array(Array(Int64)) -> Object[]

        // nested arrays are returned as nested Object[]:
        Object[] firstInner = (Object[]) arr2d[0];
        Long firstValue = (Long) firstInner[0];
    }
}

طرق GenericRecord

  • getList(...) - يقرأ أي Array(...) باعتباره List<T>. خيار افتراضي جيد لعمليات القراءة المرنة المقيّدة بالنوع. يدعم المصفوفات المتداخلة.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - الأفضل للمصفوفات أحادية البعد ذات القيم المتوافقة مع الأنواع الأولية.
  • getStringArray(...) - مخصّص لـ Array(String) (وكذلك لقيم enum الممثلة كأسماء).
  • getObjectArray(...) - خيار عام لأي نوع عنصر في Array(...)، بما في ذلك المصفوفات المتداخلة. استخدمه لقراءة المصفوفات التي تحتوي على قيم يمكن أن تكون NULL والمصفوفات المتداخلة.

تتوفر التحميلات الزائدة المستندة إلى الفهرس والمستندة إلى الاسم لجميع الطرق. يبدأ الفهرس من 1. تتيح الطرق المستندة إلى الفهرس الوصول المباشر إلى العمود. تتطلب الطرق المستندة إلى الاسم البحث عن الفهرس في كل مرة.

try (QueryResponse response = client.query("SELECT * FROM my_table").get()) {
    List<GenericRecord> rows = client.queryAll(
        "SELECT int_arr, arr2d_nullable FROM test_arrays ORDER BY id");

    for (GenericRecord row : rows) {
        Object[] intArr = row.getObjectArray("int_arr");                 // Array(Int32) -> Integer[]
        Object[] arr2d = row.getObjectArray("arr2d_nullable");           // Array(Array(Nullable(Int32)))

        Object[] inner = (Object[]) arr2d[0];
        Object maybeNull = inner[1]; // may be null
    }
}

دليل الترحيل

كان العميل القديم (V1) يستخدم com.clickhouse.client.ClickHouseClient#builder كنقطة انطلاق. أما العميل الجديد (V2) فيعتمد نمطًا مشابهًا عبر com.clickhouse.client.api.Client.Builder. وأبرز الفروقات هي:

  • لا يُستخدم أي service loader لجلب التنفيذ الفعلي. وتمثّل com.clickhouse.client.api.Client فئة واجهة لمختلف أنواع التنفيذ مستقبلًا.
  • عدد مصادر التهيئة أقل: يُمرَّر أحدها إلى الـ builder، ويكون الآخر ضمن إعدادات العملية (QuerySettings، InsertSettings). كان الإصدار السابق يتضمّن تهيئة لكل عقدة، وكان يقرأ متغيرات البيئة في بعض الحالات.

تطابق معاملات الإعداد

توجد 3 فئات enum مرتبطة بالتهيئة في V1:

  • com.clickhouse.client.config.ClickHouseDefaults - معلمات التهيئة التي يُفترض تعيينها في معظم حالات الاستخدام، مثل USER وPASSWORD.
  • com.clickhouse.client.config.ClickHouseClientOption - معلمات تهيئة خاصة بالعميل، مثل HEALTH_CHECK_INTERVAL.
  • com.clickhouse.client.http.config.ClickHouseHttpOption - معلمات تهيئة خاصة بواجهة HTTP، مثل RECEIVE_QUERY_PROGRESS.

صُمِّمت لتجميع المعاملات وتوفير فصل واضح بينها. غير أن ذلك أفضى في بعض الحالات إلى التباس (هل ثمة فرق بين com.clickhouse.client.config.ClickHouseDefaults#ASYNC وcom.clickhouse.client.config.ClickHouseClientOption#ASYNC؟). يستخدم client الجديد V2 الفئة com.clickhouse.client.api.Client.Builder بوصفها مرجعًا موحدًا لجميع خيارات تهيئة الـ client الممكنة. كما تتوفر الفئة com.clickhouse.client.api.ClientConfigProperties التي تسرد جميع أسماء معاملات التهيئة.

يوضح الجدول أدناه الخيارات القديمة المدعومة في الـ client الجديد ومعانيها الجديدة.

المفتاح: ✔ = مدعوم، ✗ = مُسقَط

إعدادات V1 أسلوب Builder في V2 ملاحظات
ClickHouseDefaults#HOST Client.Builder#addEndpoint
ClickHouseDefaults#PROTOCOL لا يدعم V2 سوى HTTP
ClickHouseDefaults#DATABASE
ClickHouseClientOption#DATABASE
Client.Builder#setDefaultDatabase
ClickHouseDefaults#USER Client.Builder#setUsername
ClickHouseDefaults#PASSWORD Client.Builder#setPassword
ClickHouseClientOption#CONNECTION_TIMEOUT Client.Builder#setConnectTimeout
ClickHouseClientOption#CONNECTION_TTL Client.Builder#setConnectionTTL
ClickHouseHttpOption#MAX_OPEN_CONNECTIONS Client.Builder#setMaxConnections
ClickHouseHttpOption#KEEP_ALIVE
ClickHouseHttpOption#KEEP_ALIVE_TIMEOUT
Client.Builder#setKeepAliveTimeout
ClickHouseHttpOption#CONNECTION_REUSE_STRATEGY Client.Builder#setConnectionReuseStrategy
ClickHouseHttpOption#USE_BASIC_AUTHENTICATION Client.Builder#useHTTPBasicAuth

الفروق العامة

  • يستخدم Client V2 عددًا أقل من الفئات الخاصة لزيادة قابلية النقل. على سبيل المثال، يعمل V2 مع أي تنفيذ لـ java.io.InputStream من أجل كتابة البيانات إلى الخادم.
  • يكون إعداد async في Client V2 مضبوطًا على off افتراضيًا. وهذا يعني عدم وجود مؤشرات ترابط إضافية وتحكمًا أكبر من التطبيق في العميل. ينبغي أن يكون هذا الإعداد off في معظم حالات الاستخدام. سيؤدي تمكين async إلى إنشاء مؤشر ترابط منفصل لكل طلب. ولا يكون ذلك منطقيًا إلا عند استخدام منفّذ يتحكم فيه التطبيق (انظر com.clickhouse.client.api.Client.Builder#setSharedOperationExecutor)

كتابة البيانات

  • استخدم أي تنفيذ لـ java.io.InputStream. الإصدار V1 com.clickhouse.data.ClickHouseInputStream مدعوم، لكنه غير مستحسن.
  • بمجرد اكتشاف نهاية مجرى الإدخال، ستتم معالجتها وفقًا لذلك. في السابق، كان يجب إغلاق مجرى الإخراج الخاص بالطلب.

V1 إدراج بيانات بتنسيق TSV.

InputStream inData = getInData();
ClickHouseRequest.Mutation request = client.read(server)
        .write()
        .table(tableName)
        .format(ClickHouseFormat.TSV);
ClickHouseConfig config = request.getConfig();
CompletableFuture<ClickHouseResponse> future;
try (ClickHousePipedOutputStream requestBody = ClickHouseDataStreamFactory.getInstance()
        .createPipedOutputStream(config)) {
    // start the worker thread which transfer data from the input into ClickHouse
    future = request.data(requestBody.getInputStream()).execute();

    // Copy data from inData stream to requestBody stream

    // We need to close the stream before getting a response
    requestBody.close();

    try (ClickHouseResponse response = future.get()) {
        ClickHouseResponseSummary summary = response.getSummary();
        Assert.assertEquals(summary.getWrittenRows(), numRows, "Num of written rows");
    }
}

إدراج بيانات بتنسيق TSV - الإصدار V2.

InputStream inData = getInData();
InsertSettings settings = new InsertSettings().setInputStreamCopyBufferSize(8198 * 2); // set copy buffer size
try (InsertResponse response = client.insert(tableName, inData, ClickHouseFormat.TSV, settings).get(30, TimeUnit.SECONDS)) {

  // Insert is complete at this point

} catch (Exception e) {
 // Handle exception
}
  • توجد طريقة واحدة فقط لاستدعائها. لا حاجة إلى إنشاء كائن طلب إضافي.
  • يُغلَق تدفّق جسم الطلب تلقائيًا عند نسخ جميع البيانات.
  • تتوفر واجهة برمجة تطبيقات جديدة منخفضة المستوى com.clickhouse.client.api.Client#insert(java.lang.String, java.util.List<java.lang.String>, com.clickhouse.client.api.DataStreamWriter, com.clickhouse.data.ClickHouseFormat, com.clickhouse.client.api.insert.InsertSettings). صُمِّم com.clickhouse.client.api.DataStreamWriter لتطبيق منطق مخصص لكتابة البيانات. على سبيل المثال، لقراءة البيانات من قائمة انتظار.

قراءة البيانات

  • تُقرأ البيانات بتنسيق RowBinaryWithNamesAndTypes افتراضيًا. وحاليًا لا يُدعَم عند الحاجة إلى ربط البيانات سوى هذا التنسيق.
  • يمكن قراءة البيانات كمجموعة من السجلات باستخدام الأسلوب List<GenericRecord> com.clickhouse.client.api.Client#queryAll(java.lang.String). يقرأ هذا الأسلوب البيانات إلى الذاكرة ثم يحرّر الاتصال. ولا حاجة إلى أي معالجة إضافية. يتيح GenericRecord الوصول إلى البيانات، ويجري بعض التحويلات.
Collection<GenericRecord> records = client.queryAll("SELECT * FROM table");
for (GenericRecord record : records) {
    int rowId = record.getInteger("rowID");
    String name = record.getString("name");
    LocalDateTime ts = record.getLocalDateTime("ts");
}

مكتبة Java client للتواصل مع خادم DB عبر بروتوكولاته. يدعم التطبيق الحالي واجهة HTTP interface فقط. توفر المكتبة واجهة برمجة تطبيقات خاصة بها لإرسال الطلبات إلى الخادم.

الإعداد

<!-- https://mvnrepository.com/artifact/com.clickhouse/clickhouse-http-client -->
<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>clickhouse-http-client</artifactId>
    <version>0.7.2</version>
</dependency>

منذ الإصدار 0.5.0، يستخدم driver مكتبة client http جديدة يجب إضافتها ضمن التبعيات.

<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5 -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.3.1</version>
</dependency>

التهيئة

صيغة URL للاتصال: protocol://host[:port][/database][?param[=value][&param[=value]][#tag[,tag]]، مثال:

  • http://localhost:8443?ssl=true&sslmode=NONE
  • https://(https://explorer@play.clickhouse.com:443

الاتصال بعقدة واحدة:

ClickHouseNode server = ClickHouseNode.of("http://localhost:8123/default?compress=0");

الاتصال بمجموعة عقد متعددة:

ClickHouseNodes servers = ClickHouseNodes.of(
    "jdbc:ch:http://server1.domain,server2.domain,server3.domain/my_db"
    + "?load_balancing_policy=random&health_check_interval=5000&failover=2");

واجهة برمجة تطبيقات الاستعلام

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from numbers limit :limit")
        .params(1000)
        .executeAndWait()) {
            ClickHouseResponseSummary summary = response.getSummary();
            long totalRows = summary.getTotalRowsToRead();
}

واجهة برمجة تطبيقات الاستعلام المتدفق

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from numbers limit :limit")
        .params(1000)
        .executeAndWait()) {
            for (ClickHouseRecord r : response.records()) {
            int num = r.getValue(0).asInteger();
            // type conversion
            String str = r.getValue(0).asString();
            LocalDate date = r.getValue(0).asDate();
        }
}

اطّلع على مثال الشيفرة الكامل في المستودع.

واجهة برمجة تطبيقات الإدراج


try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers).write()
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("insert into my_table select c2, c3 from input('c1 UInt8, c2 String, c3 Int32')")
        .data(myInputStream) // `myInputStream` is source of data in RowBinary format
        .executeAndWait()) {
            ClickHouseResponseSummary summary = response.getSummary();
            summary.getWrittenRows();
}

راجع مثال الشيفرة الكامل في المستودع.

ترميز RowBinary

يُوصَف تنسيق RowBinary في صفحته.

يمكن الاطلاع على مثال للشيفرة.

الميزات

الضغط

سيستخدم الـ client افتراضياً ضغط LZ4، مما يستلزم هذه التبعية:

<!-- https://mvnrepository.com/artifact/org.lz4/lz4-java -->
<dependency>
    <groupId>org.lz4</groupId>
    <artifactId>lz4-java</artifactId>
    <version>1.8.0</version>
</dependency>

يمكنك اختيار استخدام gzip عوضاً عن ذلك من خلال تعيين compress_algorithm=gzip في URL الاتصال.

بدلاً من ذلك، يمكنك تعطيل الضغط بعدة طرق.

  1. عطّل ذلك بتعيين compress=0 في URL الاتصال: http://localhost:8123/default?compress=0
  2. عطّل ذلك عبر إعدادات العميل:
ClickHouseClient client = ClickHouseClient.builder()
   .config(new ClickHouseConfig(Map.of(ClickHouseClientOption.COMPRESS, false)))
   .nodeSelector(ClickHouseNodeSelector.of(ClickHouseProtocol.HTTP))
   .build();

راجع وثائق الضغط للاطلاع على مزيد من المعلومات حول خيارات الضغط المختلفة.

استعلامات متعددة

تنفيذ استعلامات متعددة في خيط عامل واحدًا تلو الآخر ضمن نفس الجلسة:

CompletableFuture<List<ClickHouseResponseSummary>> future = ClickHouseClient.send(servers.apply(servers.getNodeSelector()),
    "create database if not exists my_base",
    "use my_base",
    "create table if not exists test_table(s String) engine=Memory",
    "insert into test_table values('1')('2')('3')",
    "select * from test_table limit 1",
    "truncate table test_table",
    "drop table if exists test_table");
List<ClickHouseResponseSummary> results = future.get();

المعاملات المسماة

يمكنك تمرير المعاملات بالاسم بدلاً من الاعتماد على موضعها في قائمة المعاملات فحسب. تتوفر هذه الإمكانية باستخدام دالة params.

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from my_table where name=:name limit :limit")
        .params("Ben", 1000)
        .executeAndWait()) {
            //...
        }
}

اكتشاف العقد

يوفر Java client إمكانية اكتشاف عقد ClickHouse تلقائيًا. يكون الاكتشاف التلقائي معطّلًا افتراضيًا. لتفعيله يدويًا، اضبط auto_discovery على true:

properties.setProperty("auto_discovery", "true");

أو في عنوان URL للاتصال:

jdbc:ch://my-server/system?auto_discovery=true

إذا كانت ميزة الاكتشاف التلقائي مُفعَّلةً، فلا داعي لتحديد جميع عُقد ClickHouse في URL الاتصال. ستُعامَل العُقد المحددة في URL باعتبارها seeds، وسيكتشف Java client تلقائياً المزيد من العُقد من جداول النظام و/أو clickhouse-keeper أو zookeeper.

الخيارات التالية مسؤولة عن ضبط الاكتشاف التلقائي:

الخاصية القيمة الافتراضية الوصف
auto_discovery false ما إذا كان ينبغي للعميل اكتشاف عقد إضافية من جداول النظام و/أو clickhouse-keeper/zookeeper.
node_discovery_interval 0 الفاصل الزمني لاكتشاف العقد، بالمللي ثانية. وتعني القيمة صفر أو أي قيمة سالبة أن الاكتشاف يتم لمرة واحدة.
node_discovery_limit 100 الحد الأقصى لعدد العقد التي يمكن اكتشافها في وقت واحد؛ وتشير القيمة صفر أو أي قيمة سالبة إلى عدم وجود حد.

موازنة الحمل

يختار Java client عقدةً في ClickHouse لإرسال الطلبات إليها وفقًا لسياسة موازنة الحمل. بوجهٍ عام، تكون سياسة موازنة الحمل مسؤولةً عن الأمور التالية:

  1. احصل على عقدة من قائمة العقد المُدارة.
  2. إدارة حالة العقدة.
  3. اختياريًا، قم بجدولة عملية تعمل في الخلفية لاكتشاف العقد (إذا كان الاكتشاف التلقائي مفعّلًا) وأجرِ فحصًا للحالة الصحية.

فيما يلي قائمة بالخيارات المتاحة لضبط موازنة الحمل:

الخاصية القيمة الافتراضية الوصف
load_balancing_policy "" يمكن أن تكون سياسة موازنة التحميل واحدة من التالي:
  • firstAlive - يُرسَل الطلب إلى أول عقدة سليمة من قائمة العقد المُدارة
  • random - يُرسَل الطلب إلى عقدة عشوائية من قائمة العقد المُدارة
  • roundRobin - يُرسَل الطلب إلى كل عقدة من قائمة العقد المُدارة بالتناوب.
  • الاسم المؤهل بالكامل للفئة التي تنفّذ ClickHouseLoadBalancingPolicy - سياسة موازنة تحميل مخصّصة
  • إذا لم يتم تحديدها، فسيُرسَل الطلب إلى أول عقدة في قائمة العقد المُدارة
    load_balancing_tags "" وسوم موازنة التحميل لاستبعاد العُقد. لا تُرسَل الطلبات إلا إلى العُقد التي تحمل الوسوم المحددة.
    health_check_interval 0 فاصل التحقق من السلامة بالملي ثانية؛ وتعني القيمة الصفرية أو السالبة التشغيل لمرة واحدة.
    health_check_method ClickHouseHealthCheckMethod.SELECT_ONE طريقة التحقق من السلامة. يمكن أن تكون إحدى القيم التالية:
  • ClickHouseHealthCheckMethod.SELECT_ONE - التحقق باستخدام استعلام select 1
  • ClickHouseHealthCheckMethod.PING - تحقق خاص بالبروتوكول، ويكون عمومًا أسرع
  • node_check_interval 0 فاصل التحقق من العقدة بالميلي ثانية، ويُعامَل الرقم السالب على أنه صفر. تُفحَص حالة العقدة إذا انقضت المدة المحددة منذ آخر تحقق.
    الفرق بين health_check_interval و node_check_interval هو أن الخيار health_check_interval يُجدول مهمةً تعمل في الخلفية وتتحقق من حالة قائمة العقد (كلها أو المعطوبة فقط)، بينما يحدد node_check_interval مقدار الوقت المنقضي منذ آخر تحقق لتلك العقدة بعينها
    check_all_nodes false ما إذا كان سيتم إجراء فحص السلامة على جميع العقد أم على العقد المعطلة فقط.

    التحويل التلقائي وإعادة المحاولة

    يوفر Java client خيارات التهيئة اللازمة لضبط سلوك الـ failover والـ retry للاستعلامات الفاشلة:

    الخاصية القيمة الافتراضية الوصف
    failover 0 الحد الأقصى لعدد مرات حدوث التحويل الاحتياطي للطلب. وتعني القيمة صفر أو أي قيمة سالبة عدم إجراء أي تحويل احتياطي. ويرسل التحويل الاحتياطي الطلب الذي فشل إلى عقدة أخرى (وفقًا لسياسة موازنة التحميل) للتعافي من الفشل.
    retry 0 الحد الأقصى لعدد مرات إعادة المحاولة للطلب. تعني القيمة صفر أو أي قيمة سالبة عدم إجراء أي إعادة محاولة. تُرسِل إعادة المحاولة طلبًا إلى العقدة نفسها، وذلك فقط إذا أعاد خادم ClickHouse رمز الخطأ NETWORK_ERROR
    repeat_on_session_lock true ما إذا كان ينبغي إعادة تنفيذ العملية عندما تكون الجلسة مقفلة إلى أن تنقضي المهلة (وفقًا لـ session_timeout أو connect_timeout). ويُعاد الطلب الذي فشل إذا أعاد خادم ClickHouse رمز الخطأ SESSION_IS_LOCKED

    إضافة ترويسات HTTP مخصصة

    يدعم Java client طبقة نقل HTTP/S عند الحاجة إلى إضافة HTTP headers مخصصة إلى الطلب. يجب استخدام الخاصية custom_http_headers، وينبغي الفصل بين الـ headers بفاصلة ,. أما مفتاح/قيمة الـ header فيجب الفصل بينهما باستخدام =

    دعم Java Client

    options.put("custom_http_headers", "X-ClickHouse-Quota=test, X-ClickHouse-Test=test");

    مشغّل JDBC

    properties.setProperty("custom_http_headers", "X-ClickHouse-Quota=test, X-ClickHouse-Test=test");
    Navigation