{"id":33571858,"url":"https://github.com/dolphindb/api-java","last_synced_at":"2026-04-01T19:18:49.456Z","repository":{"id":34269065,"uuid":"139784158","full_name":"dolphindb/api-java","owner":"dolphindb","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-25T01:55:39.000Z","size":5380,"stargazers_count":11,"open_issues_count":0,"forks_count":6,"subscribers_count":5,"default_branch":"master","last_synced_at":"2026-03-28T00:57:54.736Z","etag":null,"topics":["api","dolphindb","java"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dolphindb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-07-05T02:16:35.000Z","updated_at":"2026-03-26T21:40:20.000Z","dependencies_parsed_at":"2023-01-15T05:47:56.498Z","dependency_job_id":"c01f3338-8634-429c-967f-886aa4f728a6","html_url":"https://github.com/dolphindb/api-java","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"purl":"pkg:github/dolphindb/api-java","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dolphindb%2Fapi-java","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dolphindb%2Fapi-java/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dolphindb%2Fapi-java/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dolphindb%2Fapi-java/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dolphindb","download_url":"https://codeload.github.com/dolphindb/api-java/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dolphindb%2Fapi-java/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291117,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","dolphindb","java"],"created_at":"2025-11-28T10:06:03.343Z","updated_at":"2026-04-01T19:18:49.440Z","avatar_url":"https://github.com/dolphindb.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DolphinDB Java API\n\n**Note: This README documents DolphinDB Java API versions prior to 3.00.0.0. As of version 3.00.0.0, this README is no longer maintained. For documentation on the latest DolphinDB Java API, please refer to [DolphinDB Documentation](https://docs.dolphindb.com/en/javadoc/overview.html).**\n\n---\n\n- [DolphinDB Java API](#dolphindb-java-api)\n  - [1. Introduction](#1-introduction)\n  - [2. Establish DolphinDB Connection](#2-establish-dolphindb-connection)\n    - [2.1. DBConnection](#21-dbconnection)\n    - [2.2. SimpleDBConnectionPool](#22-simpledbconnectionpool)\n    - [2.3. ExclusiveDBConnectionPool](#23-exclusivedbconnectionpool)\n  - [3. Run DolphinDB Scripts](#3-run-dolphindb-scripts)\n  - [4. Execute DolphinDB Functions](#4-execute-dolphindb-functions)\n  - [5. Upload Data to DolphinDB Server](#5-upload-data-to-dolphindb-server)\n  - [6. Read Data](#6-read-data)\n  - [7. Read From and Write to DolphinDB Tables](#7-read-from-and-write-to-dolphindb-tables)\n    - [7.1. Write to an In-Memory Table](#71-write-to-an-in-memory-table)\n    - [7.2. Write to a DFS Table](#72-write-to-a-dfs-table)\n    - [7.3. Load and Query Tables](#73-load-and-query-tables)\n    - [7.4. Append Data Asynchronously](#74-append-data-asynchronously)\n  - [8. Data Type Conversion](#8-data-type-conversion)\n  - [9. Java Streaming API](#9-java-streaming-api)\n    - [9.1. Interfaces](#91-interfaces)\n    - [9.2. Code Examples](#92-code-examples)\n    - [9.3. Reconnect](#93-reconnect)\n    - [9.4. Filter](#94-filter)\n    - [9.5. Subscribe to a Heterogeneous Table](#95-subscribe-to-a-heterogeneous-table)\n    - [9.6. Unsubscribe](#96-unsubscribe)\n\n## 1. Introduction\n\nDolphinDB Java API requires Java 1.8 or higher environment. Please first declare the following Maven Dependency (version 3.00.0.0 in this example) in your project.\n\n```java\n\u003c!-- https://mvnrepository.com/artifact/com.dolphindb/dolphindb-javaapi --\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.dolphindb\u003c/groupId\u003e\n    \u003cartifactId\u003edolphindb-javaapi\u003c/artifactId\u003e\n    \u003cversion\u003e3.00.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nAs of 3.00.0.0, the method `Utils.getJavaApiVersion()` is provided to get the current Java API version.\n\nJava API adopts interface-oriented programming. It uses the interface \"Entity\" to represent all data types returned from DolphinDB. Based on \"Entity\" and DolphinDB data forms, Java API provides the following types of extended interfaces: scalar, vector, matrix, set, dictionary, table, and chart. They are included in the package of com.xxdb.data.\n\n| Extended interface classes | Naming rules              | Examples                                                |\n| :------------------------- | :------------------------ | :------------------------------------------------------ |\n| scalar                     | Basic\\\u003cDataType\u003e           | BasicInt, BasicDouble, BasicDate, etc.                  |\n| vector, matrix             | Basic\\\u003cDataType\u003e\\\u003cDataForm\u003e | BasicIntVector, BasicDoubleMatrix, BasicAnyVector, etc. |\n| set, dictionary, table     | Basic\\\u003cDataForm\u003e           | BasicSet, BasicDictionary, BasicTable                  |\n| chart                      |                           | BasicChart                                              |\n\n\"Basic\" indicates the basic implementation of a data form interface, \\\u003cDataType\\\u003e indicates a DolphinDB data type, and \\\u003cDataForm\\\u003e indicates a DolphinDB data form.\n\nThe most important object provided by DolphinDB Java API is `DBConnection`. It allows Java applications to execute scripts and functions on DolphinDB servers and transfer data between Java applications and DolphinDB servers in both directions. The DBConnection class provides the following main methods:\n\n| Method Name                                                  | Details                                  |\n| :----------------------------------------------------------- | :--------------------------------------- |\n| DBConnection( \\[asynchronousTask, useSSL, compress, usePython, sqlStd\\]) | Construct an object                      |\n| connect(host, port, \\[username, password, initialScript, enableHighAvailability, highAvailabilitySites, reconnect, enableLoadBalance\\]) | Connect the session to DolphinDB server  |\n| login(username,password,enableEncryption)                    | Log in to DolphinDB server               |\n| run(script)                                                  | Run script on DolphinDB server           |\n| run(functionName,args)                                       | Call a function on DolphinDB server      |\n| upload(variableObjectMap)                                    | Upload local data to DolphinDB server    |\n| isBusy()                                                     | Check if the current session is busy     |\n| close()                                                      | Close the current session                |\n\nThe parameter _sqlStd_ of the constructor method `DBConnection` is an enumeration type, specifying the syntax to parse input SQL scripts. Since version 1.30.22.1, three parsing syntaxes are supported: DolphinDB (default), Oracle, and MySQL. You can select the syntax by inputting the `SqlStdEnum` enumeration type.\n\nExample:\n\n```java\nDBConnection conn = new DBConnection(false, false, false, false, false, true, SqlStdEnum.DolphinDB);\n```\n\nNote: If the current session is no longer in use, Java API will automatically close the connection after a while. You can close the session by calling `close()` to release the connection. Otherwise, other sessions may be unable to connect to the server due to too many connections.\n\n## 2. Establish DolphinDB Connection\n\n### 2.1. DBConnection\n\nThe Java API connects to the DolphinDB server via TCP/IP protocol. To connect to a local DolphinDB server with port number 8848:\n\n```java\nimport com.xxdb;\nDBConnection conn = new DBConnection();\nboolean success = conn.connect(\"localhost\", 8848);\n```\n\nStarting from API 1.30.17.1, the following optional parameters can be specified for `connection`: *asynchronousTask*, *useSSL*, *compress*, and *usePython*. The default values of these parameters are false.\n\nThe following example establishes a connection to the server. It enables SSL and compression but disables asynchronous communication. Please note that the configuration parameter *enableHTTPS*=true must be specified on the server side.\n\n```java\nDBConnection conn = new DBConnection(false, true, true);\n```\n\nThe following example establishes a connection to the server. It supports asynchronous communication where no values are returned for DolphinDB scripts and functions. It is ideal for asynchronous writes.\n\n```java\nDBConnection conn = new DBConnection(true, false);\n```\n\nEstablish a connection with a username and password:\n\n```java\nboolean success = conn.connect(\"localhost\", 8848, \"admin\", \"123456\");\n```\n\nIf the connection is established without a username and password, you only have guest privileges. To be granted with more privileges, we can log in by executing `conn.login('admin', '123456', true)`.\n\nTo define and use user-defined functions in a Java program, you can pass in the user-defined scripts to the parameter initialScript. The advantages are: \n(1) These functions don't need to be defined repeatedly every time `run` is called; \n(2) The API client can automatically connect to the server after disconnection. If the parameter *initialScript* is specified, the Java API will automatically execute the script and register the functions. The parameter can be very useful for scenarios where the network is not stable but the program needs to run continuously.\n\n```java\nboolean success = conn.connect(\"localhost\", 8848, \"admin\", \"123456\", \"\");\n```\n\nTo enable high availability, set the parameter *enableHighAvailability* to true. \n\nAs of version 1.30.22.2, load balancing is automatically enabled for HA mode. Since 2.00.11.0, the `connect` method supports a new parameter *enableLoadBalance* which allows users to enable/disable load balancing in HA mode. Load balancing is only supported in HA mode and it is disabled by default.\n\nIf load balancing is disabled in HA mode, the API establishes connection to a random node. You can specify a group of nodes for connection for *highAvailabilitySites*, and the API will establish the connection to a random node from the group. If load balancing is enabled in HA mode, the API establishes connection to a low-load node. A low-load node is selected based on: memory usage\u003c80%, connections\u003c90%, and node load\u003c80%.\n\n**Note**: If a disconnection occurs, the API automatically reconnects following the above rules.\n\nFor example:\nTo enable high availability and load balancing before version 2.00.11.0:\n```java\nsites=[\"192.168.1.2:24120\", \"192.168.1.3:24120\", \"192.168.1.4:24120\"]\nboolean success = conn.connect(\"192.168.1.2\", 24120, \"admin\", \"123456\", enableHighAvailability=true, highAvailabilitySites=sites);\n```\n\nTo enable high availability and load balancing since version 2.00.11.0:\n```java\nboolean success = conn.connect(\"192.168.1.2\", 24120, \"admin\", \"123456\", enableHighAvailability=true, highAvailabilitySites=sites, enableLoadBalance=true);\n```\n\n### 2.2. SimpleDBConnectionPool\n\nStarting from version 2.00.11.1, the Java API provides connection pool `SimpleDBConnectionPool` for managing and reusing connections.\n\nFirst configure the parameters with `SimpleDBConnectionPoolConfig`, and pass the `SimpleDBConnectionPoolConfig` as the configuration for `SimpleDBConnectionPool`. After the connection pool is constructed, users can obtain a connection with `getConnection`, and release a connection with `DBConnection.close()`. When a connection is returned to the pool, it becomes idle and can be utilized later.\n\n#### 2.2.1. SimpleDBConnectionPoolConfig\n\nThe configuration can only be specified with `setXxx` method, for example:\n\n```java\nSimpleDBConnectionPoolConfig config = new SimpleDBConnectionPoolConfig();\n        config.setHostName(\"1sss\");\n        config.setPort(PORT);\n        config.setUserId(\"admin\");\n        config.setPassword(\"123456\");\n        config.setInitialPoolSize(5);\n        config.setEnableHighAvailability(false);\n```\n\nThe following parameters can be configured:\n\n- hostName: IP address. The default value is localhost.\n- port: Port number. The default value is 8848.\n- userId: User ID. The default value is \"\".\n- password: Password for the user. The default value is \"\". Only with both *userId* and *password* correctly specified can the connection log in the user. If only one of *userId* or *password* is specified, the connection will not log in the user. If the *userId* or *password* is incorrect, the connection fails.\n- initialPoolSize: Initial pool size. The default value is 5.\n- initialScript: Initial script. The default value is empty.\n- compress: Whether to compress data when downloading. The default value is false. The compress mode is more suitable for queries on large volume of data. Transferring compressed data can save network cost, but increase workloads of compression and decompression.\n- useSSL: Whether to enable SSL connection. The default value is false. To enable SSL connection, the configuration parameter *enableHTTPS* must be set to true on server.\n- usePython: Whether to enable Python Parser. The default value is false.\n- loadBalance: Whether to enable load balancing. The default value is false. If set to true, the API will perform load balancing in a polling manner across nodes specified in the *highAvailabilitySites* (or across all nodes if *highAvailabilitySites* is not specified).\n- enableHighAvailability: Whether to enable high availability. The default value is false.\n- highAvailabilitySites: A list of ip:port of all available nodes.\n\n#### 2.2.2. Methods\n\nThe following methods are provided in the `SimpleDBConnectionPool` class:\n\n| Method \t| Description \t|\n|---\t|---\t|\n| SimpleDBConnectionPool(simpleDBConnectionPoolConfig) \t| Constructor. \t|\n| DBConnection getConnection() \t| Get a connection from the pool. \t|\n| close() \t| Close a connection pool. \t|\n| isClosed() \t| Check whether the pool is closed. \t|\n| getActiveConnectionsCount() \t| Get the number of active connections. \t|\n| getIdleConnectionsCount() \t| Get the number of idle connections. \t|\n| getTotalConnectionsCount() \t| Get the size of the pool. \t|\n| DBConnection.close() \t| Release a connection from the pool. \t|\n\n**Note**: The `DBConnection.close()` method is specifically used to release a connection obtained by `getConnection`, which is different from close() of DBConnection that closes the current session.\n\n```\n// Configure the connection pool\nSimpleDBConnectionPoolConfig config = new SimpleDBConnectionPoolConfig();\n        config.setHostName(\"1sss\");\n        config.setPort(PORT);\n        config.setUserId(\"admin\");\n        config.setPassword(\"123456\");\n        config.setInitialPoolSize(5);\n        config.setEnableHighAvailability(false);\n \n// Initialize a connection pool       \nSimpleDBConnectionPool pool = new SimpleDBConnectionPool(config);\n\n// Get a connection\nDBConnection conn = pool.getConnection();\nconn.run(\"...\"); // Execute scripts\n\n// Release the connection\nconn.close();\n\n// Get the number of active connections\nint activeConns = pool.getActiveConnectionsCount();\n\n// Get the number of idle connections\nint idleConns = pool.getIdleConnectionsCount();\n\n// Close the connection pool\npool.close();\n```\n\n### 2.3. ExclusiveDBConnectionPool\n\nThe Java API provides connection pool `ExclusiveDBConnectionPool`. Users can execute a task with `execute` and then obtain the results with `getResult` method of `BasicDBTask`.\n\n| Method                                                       | Details                                                      |\n| :----------------------------------------------------------- | :----------------------------------------------------------- |\n| ExclusiveDBConnectionPool(string host, int port, string uid,string pwd, int count, bool loadBalance,bool enableHighAvailability, string\\[\\] highAvailabilitySites = null, string initialScript, bool compress = false, bool useSSL = false, bool usePython = false) | Constructor. The parameter count indicates the number of connections to be used. If loadBalance is set to true, different nodes are connected. |\n| execute(IDBTask task)                                        | Execute the task.                                            |\n| execute(List tasks)                                          | Execute tasks in batches.                                    |\n| getConnectionCount()                                         | Get the number of connections.                               |\n| shutdown()                                                   | Shut down the connection pool.                               |\n\n**Note**: If the current `ExclusiveDBConnectionPool` is no longer in use, Java API will automatically close the connection after a while. To release the connection resources, call `shutdown()`upon the completion of thread tasks. \n\n`BasicDBTask` wraps the functions and arguments to be executed.\n\n| Method Name                           | Details                                                      |\n| :------------------------------------ | :----------------------------------------------------------- |\n| BasicDBTask(string script, List args) | script: the function to be executed; args: the arguments passed. |\n| BasicDBTask(string script)            | The script to be executed.                                   |\n| isSuccessful()                        | Check whether the task is executed successfully.             |\n| getResult()                           | Get the execution results.                                   |\n| getErrorMsg()                         | Get the error messages.                                      |\n\nBuild a connection pool with 10 connections.\n\n```java\nExclusiveDBConnectionPool pool = new ExclusiveDBConnectionPool(hostName, port, userName, passWord, 10, false, false);\n```\n\nCreate a task.\n\n```java\nBasicDBTask task = new BasicDBTask(\"1..10\");\npool.execute(task);\n```\n\nCheck whether the task is executed successfully. If successful, return the results; otherwise return the error messages.\n\n```java\nBasicIntVector data = null;\nif (task.isSuccessful()) {\n    data = (BasicIntVector)task.getResult();\n} else {\n    throw new Exception(task.getErrorMsg());\n}\nSystem.out.print(data.getString());\n```\n\nOutput:\n\n```\n[1,2,3,4,5,6,7,8,9,10]\n```\n\nCreate multiple tasks and call these tasks concurrently in ExclusiveDBConnectionPool.\n\n```java\nList\u003cDBTask\u003e tasks = new ArrayList\u003c\u003e();\nfor (int i = 0; i \u003c 10; ++i){\n    //call function log\n    tasks.add(new BasicDBTask(\"log\", Arrays.asList(data.get(i))));\n}\npool.execute(tasks);\n```\n\nCheck whether the task is executed successfully. If successful, return the results; otherwise return the error messages.\n\n```java\nList\u003cEntity\u003e result = new ArrayList\u003c\u003e();\nfor (int i = 0; i \u003c 10; ++i)\n{\n    if (tasks.get(i).isSuccessful())\n    {\n        result.add(tasks.get(i).getResult());\n    }\n    else\n    {\n        throw new Exception(tasks.get(i).getErrorMsg());\n    }\n    System.out.println(result.get(i).getString());\n}\n```\n\nOutput:\n\n```\n0\n0.693147\n1.098612\n1.386294\n1.609438\n1.791759\n1.94591\n2.079442\n2.197225\n2.302585\n```\n\n## 3. Run DolphinDB Scripts\n\nTo run DolphinDB script in Java:\n\n```java\nconn.run(\"script\");\n```\n\nBefore version 2.00.11.0, the `run` method automatically enables sequence number. The number is a LONG integer that represents the task sequence number for a client. If a write task fails, the task will be resubmitted. However, in cases like writing multiple tables at once, data loss may occur.\n\nSince version 2.00.11.0, the `run` method supports a new parameter enableSeqNo which allows users to enable/disable the sequence number feature. For example:\n\n```java\npublic Entity run(String script, ProgressListener listener, int priority, int \nparallelism, int fetchSize, boolean clearSessionMemory, String tableName, boolean enableSeqNo)\n```\n\n## 4. Execute DolphinDB Functions\n\nOther than running script, method `run` can also execute DolphinDB built-in functions or user-defined functions on a remote DolphinDB server. If method `run` has only one parameter, the parameter is a script. If method `run` has 2 parameters, the first parameter is a DolphinDB function name and the second parameter is the function's arguments.\n\nNote: Please make sure that there are no extra spaces before and after the *function* parameter, and the specified function must exist. Otherwise, the following error will occur when executing `DBConnection.run(String function, List\u003cEntity\u003e arguments)`:\n\n```java\nServer response: 'Can't recognize function name functionA ' function: 'functionA '\n```\n\nThe following examples illustrate 3 ways to call DolphinDB's built-in function `add` in Java, depending on the locations of the parameters \"x\" and \"y\" of function `add`.\n\n- Both parameters are on DolphinDB server\n\nIf both variables \"x\" and \"y\" have been generated on DolphinDB server by Java applications,\n\n```java\nconn.run(\"x = [1,3,5];y = [2,4,6]\")\n```\n\nthen we can execute run(\"script\") directly.\n\n```java\npublic void testFunction() throws IOException{\n    Vector result = (Vector)conn.run(\"add(x,y)\");\n    System.out.println(result.getString());\n}\n```\n\n- Only 1 parameter exists on DolphinDB server\n\nParameter \"x\" was generated on DolphinDB server by the Java program, and parameter \"y\" is to be generated by the Java program.\n\n```java\nconn.run(\"x = [1,3,5]\")\n```\n\nIn this case, we need to use \"partial application\" to embed parameter \"x\" in function `add`. For details, please refer to [Partial Application Documentation](https://www.dolphindb.com/help/Functionalprogramming/PartialApplication.html).\n\n```java\npublic void testFunction() throws IOException{\n    List\u003cEntity\u003e args = new ArrayList\u003cEntity\u003e(1);\n    BasicDoubleVector y = new BasicDoubleVector(3);\n    y.setDouble(0, 2.5);\n    y.setDouble(1, 3.5);\n    y.setDouble(2, 5);\n    args.add(y);\n    Vector result = (Vector)conn.run(\"add{x}\", args);\n    System.out.println(result.getString());\n}\n```\n\n- Both parameters are to be generated by Java program\n\n```java\nimport java.util.List;\nimport java.util.ArrayList;\n\npublic void testFunction() throws IOException{\n    List\u003cEntity\u003e args = new ArrayList\u003cEntity\u003e(1);\n    BasicDoubleVector x = new BasicDoubleVector(3);\n    x.setDouble(0, 1.5);\n    x.setDouble(1, 2.5);\n    x.setDouble(2, 7);\n    BasicDoubleVector y = new BasicDoubleVector(3);\n    y.setDouble(0, 2.5);\n    y.setDouble(1, 3.5);\n    y.setDouble(2, 5);\n    args.add(x);\n    args.add(y);\n    Vector result = (Vector)conn.run(\"add\", args);\n    System.out.println(result.getString());\n}\n```\n\nBefore version 2.00.11.0, the `run` method automatically enables sequence number. Since version 2.00.11.0, the `run` method supports a new parameter enableSeqNo which allows users to enable/disable the sequence number feature. For example:\n\n```\npublic Entity run(String function, List\u003cEntity\u003e arguments, int priority, int parallelism, int fetchSize, boolean enableSeqNo)\n```\n\n## 5. Upload Data to DolphinDB Server\n\nWe can upload a data object to DolphinDB server and assign it to a variable for future use. Variable names can use 3 types of characters: letters, numbers and underscores. The first character must be a letter.\n\n```java\npublic void testFunction() throws IOException{\n    Map\u003cString, Entity\u003e vars = new HashMap\u003cString, Entity\u003e();\n    BasicDoubleVector vec = new BasicDoubleVector(3);\n    vec.setDouble(0, 1.5);\n    vec.setDouble(1, 2.5);\n    vec.setDouble(2, 7);\n    vars.put(\"a\",vec);\n    conn.upload(vars);\n    Entity result = conn.run(\"accumulate(+,a)\");\n    System.out.println(result.getString());\n}\n``` \n\n## 6. Read Data\n\nThis section introduces how to read different DolphinDB data forms with the `DBConnection` object.\n\nImport the DolphinDB data type package:\n\n```\nimport com.xxdb.data.*;\n```\n\n- Vector\n\nThe following DolphinDB statement returns a Java object BasicStringVector.\n\n```\nrand(`IBM`MSFT`GOOG`BIDU,10)\n```\n\nThe `rows` method returns the size of a vector. We can access an element by index with the `getString` method.\n\n```java\npublic void testStringVector() throws IOException{\n    BasicStringVector vector = (BasicStringVector)conn.run(\"rand(`IBM`MSFT`GOOG`BIDU, 10)\");\n    int size = vector.rows();\n    System.out.println(\"size: \"+size);\n    for(int i=0; i\u003csize; ++i)\n        System.out.println(vector.getString(i));\n}\n```\n\nSimilarly, we can work with vectors or tuples of INT, DOUBLE, FLOAT or any other types.\n\n```java\npublic void testDoubleVector() throws IOException{\n    BasicDoubleVector vector = (BasicDoubleVector)conn.run(\"rand(10.0, 10)\");\n    int size = vector.rows();\n    System.out.println(\"size: \"+size);\n    for(int i=0; i\u003csize; ++i)\n       System.out.println(vector.getDouble(i));\n}\n```\n\nFor the tuple \\[`GS, 2, \\[1,3,5\\],\\[0.9, \\[0.8\\]\\]\\], the following script gets the data form, data type and contents of the third element:\n\n```java\npublic void testAnyVector() throws IOException{\n    \n    BasicAnyVector result = (BasicAnyVector)conn.run(\"[`GS, 2, [1,3,5],[0.9, [0.8]]]\");\n    \n  System.out.println(result.getEntity(2).getDataForm()); //DF_VECTOR\n\tSystem.out.println(result.getEntity(2).getDataType()); //DT_INT\n\tSystem.out.println(result.getEntity(2).getString()); //\"[1,3,5]\"\n\tSystem.out.println(((BasicIntVector)result.getEntity(2)).getInt(0)); //1\n\tSystem.out.println(((BasicIntVector)result.getEntity(2)).getInt(1)); //3\n\tSystem.out.println(((BasicIntVector)result.getEntity(2)).getInt(2)); //5\n}\n```\n\n- Set\n\n```java\npublic void testSet() throws IOException{\n\tBasicSet result = (BasicSet)conn.run(\"set(1..100)\");\n\tSystem.out.println(result.rows()==100);\n\tSystem.out.println(((BasicInt)result.keys().get(0)).getInt()==1);\n}\n```\n\n- Matrix\n\nTo retrieve an element from an integer matrix, we can use `getInt`. To get the number of rows and columns of a matrix, we can use functions `rows` and `columns`, respectively.\n\n```java\npublic void testIntMatrix() throws IOException {\n\tBasicIntMatrix matrix = (BasicIntMatrix)conn.run(\"1..6$3:2\");\n\tSystem.out.println(matrix.getInt(0,1)==4);\n\tSystem.out.println(matrix.rows()==3);\n\tSystem.out.println(matrix.columns()==2);\n}\n```\n\n- Dictionary\n\nThe keys and values of a dictionary can be retrieved with functions `keys` and `values`, respectively. To get the value for a key, use `get`.\n\n```java\npublic void testDictionary() throws IOException{\n\t\tBasicDictionary dict = (BasicDictionary)conn.run(\"dict(1 2 3,`IBM`MSFT`GOOG)\");\n        System.out.println(dict.keys());  //[1, 2, 3]\n\t\tSystem.out.println(dict.values()); //[IBM, MSFT, GOOG]\n\t\t//to print the corresponding value for key 1.\n\t\tSystem.out.println(dict.get(new BasicInt(1)).getString()); //IBM\n}\n```\n\n- Table\n\nTo get a column of a table, use `getColumn`; to get a column name, use `getColumnName`. To get the number of columns and rows of a table, use `columns` and `rows`, respectively.\n\n```java\npublic void testTable() throws IOException{\n    StringBuilder sb =new StringBuilder();\n    sb.append(\"n=2000\\n\");\n    sb.append(\"syms=`IBM`C`MS`MSFT`JPM`ORCL\\n\");\n    sb.append(\"mytrades=table(09:30:00+rand(18000,n) as timestamp,rand(syms, n) as sym, 10*(1+rand(100,n)) as qty,5.0+rand(100.0,n) as price)\\n\");\n    sb.append(\"select qty,price from mytrades where sym=`IBM\");\n    BasicTable table = (BasicTable)conn.run(sb.toString());\n    System.out.println(table.getString());\n}\n```\n\n- NULL object\n\nTo determine if an object is NULL, use `getDataType`.\n\n```java\npublic void testVoid() throws IOException{\n\tEntity obj = conn.run(\"NULL\");\n\tSystem.out.println(obj.getDataType().equals(Entity.DATA_TYPE.DT_VOID)); //true\n}\n```\n\n## 7. Read From and Write to DolphinDB Tables\n\nThere are 2 types of DolphinDB tables:\n\n- In-memory table: it has the fastest access speed, but if the node shuts down the data will be lost.\n- DFS table: data are distributed across disks of multiple nodes. \n\n### 7.1. Write to an In-Memory Table\n\nDolphinDB offers several ways to write to an in-memory table:\n\n- Insert a single row of data with `insert into`\n- Insert multiple rows of data in bulk with function `tableInsert`\n- Insert a table object with function `tableInsert`\n\nIt is not recommended to save data with function `append!`, as `append!` returns the schema of a table and unnecessarily increases the network traffic.\n\nThe table in the following examples has 4 columns. Their data types are STRING, INT, TIMESTAMP and DOUBLE. The column names are cstring, cint, ctimestamp and cdouble, respectively.\n\n```\nt = table(10000:0,`cstring`cint`ctimestamp`cdouble,[STRING,INT,TIMESTAMP,DOUBLE])\nshare t as sharedTable\n```\n\nBy default, an in-memory table is not shared among sessions. To access it in a different session, share it among sessions with `share`.\n\n#### 7.1.1. Insert a Single Record with `insert into`\n\nTo insert a single record to a DolphinDB in-memory table, you can use the `insert into` statement.\n\n```java\npublic void test_save_Insert(String str,int i, long ts,double dbl) throws IOException{\n    conn.run(String.format(\"insert into sharedTable values('%s',%s,%s,%s)\",str,i,ts,dbl));\n}\n```\n\n#### 7.1.2. Insert Multiple Records in Bulk with `tableInsert`\n\nFunction `tableInsert` can save records in batches. If data in Java can be organized as a List, it can be saved with function `tableInsert`.\n\n```java\npublic void test_save_TableInsert(List\u003cString\u003e strArray,List\u003cInteger\u003e intArray,List\u003cLong\u003e tsArray,List\u003cDouble\u003e dblArray) throws IOException{\n    //Construct parameters with arrays\n    List\u003cEntity\u003e args = Arrays.asList(new BasicStringVector(strArray),new BasicIntVector(intArray),new BasicTimestampVector(tsArray),new BasicDoubleVector(dblArray));\n    conn.run(\"tableInsert{sharedTable}\", args);\n}\n```\n\nThe example above uses partial application in DolphinDB to embed a table in `tableInsert{sharedTable}` as a function. For details about partial application, please refer to [Partial Application Documentation](https://www.dolphindb.com/help/Functionalprogramming/PartialApplication.html).\n\n#### 7.1.3. Save BasicTable Objects With Function `tableInsert`\n\nFunction `tableInsert` can also accept a BasicTable object in Java as a parameter to append data to a table in batches. \n\n```java\npublic void test_save_table(BasicTable table1) throws IOException {\n    List\u003cEntity\u003e args = Arrays.asList(table1);\n    conn.run(\"tableInsert{shareTable}\", args);\n}\n```\n\n### 7.2. Write to a DFS Table\n\nDFS table is recommended by DolphinDB in production environment. It supports snapshot isolation and ensures data consistency. With data replication, DFS tables offers fault tolerance and load balancing.\n\n#### 7.2.1. Save BasicTable Objects With Function `tableInsert`\n\n\n```java\ndbPath = 'dfs://testDatabase'\ntbName = 'tb1'\n\nif(existsDatabase(dbPath)){dropDatabase(dbPath)}\ndb = database(dbPath,RANGE,2018.01.01..2018.12.31)\ndb.createPartitionedTable(t,tbName,'ctimestamp')\n```\n\nDolphinDB provides `loadTable` method to load DFS tables and `tableInsert` method to append data.\n\n```java\npublic void test_save_table(String dbPath, BasicTable table1) throws IOException{\n    List\u003cEntity\u003e args = new ArrayList\u003cEntity\u003e(1);\n    args.add(table1);\n    conn.run(String.format(\"tableInsert{loadTable('%s','tb1')}\",dbPath), args);\n}\n```\n\nYou can conveniently construct a BasicTable object with arrays or lists in Java to be appended to DFS tables. For example, the following 5 list objects boolArray, intArray, dblArray, dateArray and strArray are used to construct a BasicTable object:\n\n```java\nList\u003cString\u003e colNames =  Arrays.asList(\"cbool\",\"cint\",\"cdouble\",\"cdate\",\"cstring\");\nList\u003cVector\u003e cols = Arrays.asList(new BasicBooleanVector(boolArray),new BasicIntVector(intArray),new BasicDoubleVector(dblArray),new BasicDateVector(dateArray),new BasicStringVector(strArray));\nBasicTable table1 = new BasicTable(colNames,cols);\n```\n\n\n#### 7.2.2. Append to DFS Tables\n\nDolphinDB DFS tables support concurrent reads and writes. This section introduces how to write data concurrently to DolphinDB DFS tables in Java.\n\nPlease note that multiple writers are not allowed to write to one partition at the same time in DolphinDB. Therefore, please make sure that each thread writes to a different partition separately when the client uses multiple writer threads.\n\nDolphinDB's Java API offers a convenient way to separate data by partition and write concurrently:\n\n```java\npublic PartitionedTableAppender(String dbUrl, String tableName, String partitionColName, String appendFunction, DBConnectionPool pool)\n```\n\n- **dbUrl:** DFS database path\n- **tableName:** DFS table name\n- **partitionColName:** partitioning column\n- **appendFunction:** (optional) a user-defined function. `tableInsert` is called by default.\n- **pool:** connection pool for concurrent writes\n\n```java\nDBConnectionPool pool = new ExclusiveDBConnectionPool(HOST, PORT, \"admin\", \"123456\", 3, true, true);\nPartitionedTableAppender appender = new PartitionedTableAppender(dbUrl, tableName , \"sym\", pool);\n```\n\nThe following script first creates a DFS database \"dfs://DolphinDBUUID\" and a partitioned table \"device_status\". The database uses a COMPO domain of VALUE-HASH-HASH.\n\n```\nt = table(timestamp(1..10)  as date,string(1..10) as sym)\ndb1=database(\\\"\\\",HASH,[DATETIME,10])\ndb2=database(\\\"\\\",HASH,[STRING,5])\nif(existsDatabase(\\\"dfs://demohash\\\")){\n    dropDatabase(\\\"dfs://demohash\\\")\n}\ndb =database(\\\"dfs://demohash\\\",COMPO,[db2,db1])\npt = db.createPartitionedTable(t,`pt,`sym`date)\n```\n\nWith DolphinDB server version 1.30 or above, you can write to DFS tables with the `PartitionedTableAppender` object in Java API. The user needs to first specify a connection pool, and the system obtains information about partitions before assigning the partitions to the connection pool for concurrent writes. A partition can only be written by one thread at a time. For example:\n\n```java\nDBConnectionPool conn = new ExclusiveDBConnectionPool(host, Integer.parseInt(port), \"admin\", \"123456\", Integer.parseInt(threadCount), false, false);\n\nPartitionedTableAppender appender = new PartitionedTableAppender(dbPath, tableName, \"gid\", \"saveGridData{'\" + dbPath + \"','\" + tableName + \"'}\", conn);\nBasicTable table1 = createTable();\nappender.append(table1);                   \n```\n\n### 7.3. Load and Query Tables\n\n#### 7.3.1. Load Tables\n\nTo load a DFS table in Java API, you can execute the following code to read the table as a whole.\n\n```java\nString dbPath = \"dfs://testDatabase\";\nString tbName = \"tb1\";\nDBConnection conn = new DBConnection();\nconn.connect(SERVER, PORT, USER, PASSWORD);\nBasicTable table = (BasicTable)conn.run(String.format(\"select * from loadTable('%s','%s') where cdate = 2017.05.03\",dbPath,tbName));\n```\n\nStarting from DolphinDB server 1.20.5, you can also load a large table in blocks.\n\nSet the parameter *fetchSize* of `run` method to specify the size of a block, and the method returns an EntityBlockReader object. Use the `read` method to read data in blocks.\n\n```java\nDBConnection conn = new DBConnection();\nconn.connect(SERVER, PORT, USER, PASSWORD);\nEntityBlockReader v = (EntityBlockReader)conn.run(\"table(1..22486 as id)\",(ProgressListener) null,4,4,10000);\nBasicTable data = (BasicTable)v.read();\nwhile(v.hasNext()){\n    BasicTable t = (BasicTable)v.read();\n    data = data.combine(t);\n}\n```\n\nWhen using the above method to read data in blocks, if not all blocks are read, please call the `skipAll` method to abort the reading before executing the subsequent code. Otherwise, data will be stuck in the socket buffer and the deserialization of the subsequent data will fail.\n\n```java\n  EntityBlockReader v = (EntityBlockReader)conn.run(\"table(1..12486 as id)\",(ProgressListener) null,4,4,10000);\n  BasicTable data = (BasicTable)v.read();\n  v.skipAll();\n  BasicTable t1 = (BasicTable)conn.run(\"table(1..100 as id1)\");  \n```\n\n#### 7.3.2. Use BasicTable Object\n\nIn Java API, a table is saved as a BasicTable object. Since BasicTable is column-based, to retrieve rows via the Java API, you need to access the columns first and then get the rows.\n\nIn the example below, the BasicTable has 4 columns with data types STRING, INT, TIMESTAMP and DOUBLE. The column names are cstring, cint, ctimestamp and cdouble.\n\n```java\npublic void test_loop_basicTable(BasicTable table1) throws Exception{\n    BasicStringVector stringv = (BasicStringVector) table1.getColumn(\"cstring\");\n    BasicIntVector intv = (BasicIntVector) table1.getColumn(\"cint\");\n    BasicTimestampVector timestampv = (BasicTimestampVector) table1.getColumn(\"ctimestamp\");\n    BasicDoubleVector doublev = (BasicDoubleVector) table1.getColumn(\"cdouble\");\n    for(int ri=0; ri\u003ctable1.rows(); ri++){\n        System.out.println(stringv.getString(ri));\n        System.out.println(intv.getInt(ri));\n        LocalDateTime timestamp = timestampv.getTimestamp(ri);\n        System.out.println(timestamp);\n        System.out.println(doublev.getDouble(ri));\n    }\n}\n```\n\n### 7.4. Append Data Asynchronously\n\nYou can use methods of `MultithreadedTableWriter` class to asynchronously append data to a DolphinDB in-memory table, dimension table, or a DFS table. The class maintains a buffer queue. Even when the server is fully occupied with network I/O operations, the writing threads of the API client will not be blocked. \n\nFor asynchronous writes:\n\n- When the API client submits the task to the buffer queue, the task is considered as completed.\n- You can check the status with `getStatus`.\n\n#### 7.4.1. MultithreadedTableWriter\n\n`MultithreadedTableWriter` supports concurrent writes in multiple threads.\n\nThe methods of `MultithreadedTableWriter` object are introduced as follows:\n\n```java\npublic MultithreadedTableWriter(String hostName, int port, String userId, String password,\n    String dbName, String tableName, boolean useSSL,\n    boolean enableHighAvailability, String[] highAvailabilitySites,\n    int batchSize, float throttle,\n    int threadCount, String partitionCol,\n    int[] compressTypes, Mode mode, String[] pModeOption,\n    boolean enableActualSendTime)\n```\n\nParameters:\n\n- **hostName**: host name\n- **port**: port number\n- **userId** / **password**: username and password\n- **dbPath**: a STRING indicating the DFS database path. Leave it unspecified for an in-memory table.\n- **tableName**: a STRING indicating the in-memory or DFS table name. \n\n**Note:** For API 1.30.17 or lower versions, when writing to an in-memory table, please specify the in-memory table name for *dbPath* and leave *tableName* empty.\n\n- **useSSL**: a Boolean value indicating whether to enable SSL. The default value is false.\n- **enableHighAvailability**: a Boolean value indicating whether to enable high availability. The default value is false.\n- **highAvailabilitySites**: a list of ip:port of all available nodes\n- **batchSize**: an integer indicating the number of messages in batch processing. The default value is 1, meaning the server processes the data as soon as they are written. If it is greater than 1, only when the number of data reaches *batchSize*, the client will send the data to the server.\n- **throttle**: a positive floating-point number indicating the waiting time (in seconds) before the server processes the incoming data if the number of data written from the client does not reach *batchSize*.\n- **threadCount**: an integer indicating the number of working threads to be created. The default value is 1, indicating single-threaded process. It must be 1 for a dimension table.\n- **partitionCol**: a STRING indicating the partitioning column. It is None by default, and only takes effect when *threadCount* is greater than 1. For a partitioned table, it must be the partitioning column; for a stream table, it must be a column name; for a dimension table, the parameter does not take effect.\n- **compressMethods**: an array of the compression methods used for each column. If unspecified, the columns are not compressed. The compression methods (case-insensitive) include:\n  - \"Vector.COMPRESS_LZ4\": LZ4 algorithm\n  - \"Vector.COMPRESS_DELTA\": Delta-of-delta encoding\n- **enableActualSendTime**: a Boolean value that specifies whether to record the send time for each message. Note that the last column of tableName must be of NANOTIMESTAMP type.\n\nThe following part introduces methods of `MultithreadedTableWriter` object.\n\n(1) insert\n\n```java\nErrorCodeInfo insert(Object... args)\n```\n\nDetails:\n\nInsert a single record. Return a class `ErrorCodeInfo` containing *errorCode* and *errorInfo*. If *errorCode* is not \"\", `MultithreadedTableWriter` has failed to insert the data, and *errorInfo* displays the error message.\n\nThe class `ErrorCodeInfo` provides methods `hasError()` and `succeed()` to check whether the data is written properly. `hasError()` returns true if an error occurred, false otherwise. `succeed()` returns true if the data is written successfully, false otherwise.\n\n**Parameters:**\n\n- **args**: a variable-length argument (varargs) indicating the record to be inserted.\n\n**Examples:**\n\n```java\nErrorCodeInfo pErrorInfo = multithreadedTableWriter_.insert(new Date(2022, 3, 23), \"AAAAAAAB\", random.nextInt() % 10000);\n```\n\n\n(2) getUnwrittenData\n\n```java\nList\u003cList\u003cEntity\u003e\u003e getUnwrittenData()\n```\n\n**Details:**\n\nReturn a nested list of data that has not been written to the server.\n\n**Note:** Data obtained by this method will be released by `MultithreadedTableWriter`.\n\n**Examples:**\n\n```java\nList\u003cList\u003cEntity\u003e\u003e unwrittenData = multithreadedTableWriter_.getUnwrittenData();\n```\n\n\n(3) insertUnwrittenData\n\n```java\nErrorCodeInfo insertUnwrittenData(List\u003cList\u003cEntity\u003e\u003e records)\n```\n\n**Details:** \n\nInsert unwritten data. The result is in the same format as `insert`. The difference is that `insertUnwrittenData` can insert multiple records at a time.\n\n**Parameters:**\n\n- **records**: the data that has not been written to the server. You can obtain the object with method `getUnwrittenData`.\n\n**Examples:**\n\n```java\nErrorCodeInfo ret = multithreadedTableWriter_.insertUnwrittenData(unwrittenData);\n```\n\n(4) getStatus\n\n```java\nStatus getStatus()\n```\n\n**Details:** \n\nGet the current status of the `MultithreadedTableWriter` object.\n\n**Parameters:**\n\n- **status**: the `MultithreadedTableWriter.Status` class with the following attributes and methods:\n\nFor example\n\n```java\nMultithreadedTableWriter.Status writeStatus = new MultithreadedTableWriter.Status();\nwriteStatus = multithreadedTableWriter_.getStatus();\n```\n\n**Attributes:**\n\n- **isExiting:** whether the threads are exiting\n- **errorCode:** error code\n- **errorInfo:** error message\n- **sentRows:** number of sent rows\n- **unsentRows:** number of rows to be sent\n- **sendFailedRows:** number of rows failed to be sent\n- **threadStatus:** a list of the thread status\n  - threadId: thread ID\n  - sentRows: number of rows sent by the thread\n  - unsentRows: number of rows to be sent by the thread\n  - sendFailedRows: number of rows failed to be sent by the thread\n\n**Methods:**\n\n- `hasError()`: return true if an error occurred, false otherwise.\n- `succeed()`: return true if the data is written successfully, false otherwise.\n\n \n(5) waitForThreadCompletion\n\n```java\nwaitForThreadCompletion()\n```\n\n**Details:** \n\nAfter calling the method, `MultithreadedTableWriter` will wait until all working threads complete their tasks. If you call `insert` or `insertUnwrittenData` after the execution of `waitForThreadCompletion`, an error \"thread is exiting\" will be raised.\n\n**Examples:**\n\n```java\nmultithreadedTableWriter_.waitForThreadCompletion();\n```\n\nThe methods of `MultithreadedTableWriter` are usually used in the following way:\n\n```java\nDBConnection conn= new DBConnection();\nconn.connect(HOST, PORT, \"admin\", \"123456\");\nRandom random = new Random();\nString script =\n        \"dbName = 'dfs://valuedb3'\" +\n                \"if (exists(dbName))\" +\n                \"{\" +\n                \"dropDatabase(dbName);\" +\n                \"}\" +\n                \"datetest = table(1000:0,`date`symbol`id,[DATE, SYMBOL, LONG]);\" +\n                \"db = database(directory= dbName, partitionType= HASH, partitionScheme=[INT, 10]);\" +\n                \"pt = db.createPartitionedTable(datetest,'pdatetest','id');\";\nconn.run(script);\nMultithreadedTableWriter multithreadedTableWriter_ = new MultithreadedTableWriter(HOST, PORT, \"admin\", \"123456\", \"dfs://valuedb3\", \"pdatetest\",\n        false, false, null, 10000, 1,\n        5, \"id\", new int[]{Vector.COMPRESS_LZ4, Vector.COMPRESS_LZ4, Vector.COMPRESS_DELTA});\nErrorCodeInfo ret;\ntry\n{\n    //insert 100 rows of records with correct data types and column count\n    for (int i = 0; i \u003c 100; ++i)\n    {\n        ret = multithreadedTableWriter_.insert(new Date(2022, 3, 23), \"AAAAAAAB\", random.nextInt() % 10000);\n    }\n} \ncatch (Exception e)\n{   //MTW raises an exception\n    System.out.println(\"MTW exit with exception {0}\" + e.getMessage());\n}\n\n//wait until MTW finishes writing\nmultithreadedTableWriter_.waitForThreadCompletion();\nMultithreadedTableWriter.Status writeStatus = new MultithreadedTableWriter.Status();\nwriteStatus = multithreadedTableWriter_.getStatus();\nif (!writeStatus.errorInfo.equals(\"\"))\n{\n    //if error occured in writing\n    System.out.println(\"error in writing !\");\n}\nSystem.out.println(\"writeStatus: {0}\\n\" + writeStatus.toString());\nSystem.out.println(((BasicLong)conn.run(\"exec count(*) from pt\")).getLong());\n```\n\nOutput:\n\n```\n\"\"\"\n      writeStatus: {0}\n      errorCode     : \n      errorInfo     : \n      isExiting     : true\n      sentRows      : 100\n      unsentRows    : 0\n      sendFailedRows: 0\n      threadStatus  :\n              threadId        sentRows      unsentRows  sendFailedRows\n                    13              30               0               0\n                    14              18               0               0\n                    15              15               0               0\n                    16              20               0               0\n                    17              17               0               0\n    \n      100\n\"\"\"\n```\n\nThe above example calls method `writer.insert()` to write data to writer, and obtains the status with `writer.getStatus()`. Please note that the method `writer.waitForThreadCompletion()` will wait for `MultithreadedTableWriter` to finish the data writes, and then terminate all working threads with the last status retained. A new MTW object must be created to write data again.\n\nAs shown in the above example, `MultithreadedTableWriter` applies multiple threads to data conversion and writes. The API client also uses multiple threads to call `MultithreadedTableWriter`, and the implementation is thread-safe.\n \n#### 7.4.2. Exceptions Raised by MultithreadedTableWriter\n\nWhen calling method `insert` of class `MultithreadedTableWriter`:\n\nIf the inserted data type does match the data type of the corresponding column, `MultithreadedTableWriter` immediately returns an error message and prints a stack.\n\nFor example:\n\n```java\nDBConnection conn= new DBConnection();\nconn.connect(HOST, PORT, \"admin\", \"123456\");\nRandom random = new Random();\nString script =\n        \"dbName = 'dfs://valuedb3'\" +\n                \"if (exists(dbName))\" +\n                \"{\" +\n                \"dropDatabase(dbName);\" +\n                \"}\" +\n                \"datetest = table(1000:0,`date`symbol`id,[DATE, SYMBOL, LONG]);\" +\n                \"db = database(directory= dbName, partitionType= HASH, partitionScheme=[INT, 10]);\" +\n                \"pt = db.createPartitionedTable(datetest,'pdatetest','id');\";\nconn.run(script);\nMultithreadedTableWriter multithreadedTableWriter_ = new MultithreadedTableWriter(HOST, PORT, \"admin\", \"123456\", \"dfs://valuedb3\", \"pdatetest\",\n        false, false, null, 10000, 1,\n        5, \"id\", new int[]{Vector.COMPRESS_LZ4, Vector.COMPRESS_LZ4, Vector.COMPRESS_DELTA});\nErrorCodeInfo ret;\n//MTW returns the error message\nret = multithreadedTableWriter_.insert(new Date(2022, 3, 23), 222, random.nextInt() % 10000);\nif (!ret.errorInfo.equals(\"\"))\n    System.out.println(\"insert wrong format data: {2}\\n\" + ret.toString());\n```\n\nOutput:\n\n```\n\"\"\"\n      java.lang.RuntimeException: Failed to insert data. Cannot convert int to DT_SYMBOL.\n      \tat com.xxdb.data.BasicEntityFactory.createScalar(BasicEntityFactory.java:795)\n      \tat com.xxdb.data.BasicEntityFactory.createScalar(BasicEntityFactory.java:505)\n      \tat com.xxdb.multithreadedtablewriter.MultithreadedTableWriter.insert(MultithreadedTableWriter.java:594)\n      \tat com.xxdb.BehaviorTest.testMul(BehaviorTest.java:89)\n      \tat com.xxdb.BehaviorTest.main(BehaviorTest.java:168)\n        code=A1 info=Invalid object error java.lang.RuntimeException: Failed to insert data. Cannot convert int to DT_SYMBOL.\n\"\"\"\n```\n\nIf the number of inserted columns does not match that of the table when the method `insert` is called, the error message is thrown immediately.\n\nFor example:\n\n```java\nDBConnection conn= new DBConnection();\nconn.connect(HOST, PORT, \"admin\", \"123456\");\nRandom random = new Random();\nString script =\n        \"dbName = 'dfs://valuedb3'\" +\n                \"if (exists(dbName))\" +\n                \"{\" +\n                \"dropDatabase(dbName);\" +\n                \"}\" +\n                \"datetest = table(1000:0,`date`symbol`id,[DATE, SYMBOL, LONG]);\" +\n                \"db = database(directory= dbName, partitionType= HASH, partitionScheme=[INT, 10]);\" +\n                \"pt = db.createPartitionedTable(datetest,'pdatetest','id');\";\nconn.run(script);\nMultithreadedTableWriter multithreadedTableWriter_ = new MultithreadedTableWriter(HOST, PORT, \"admin\", \"123456\", \"dfs://valuedb3\", \"pdatetest\",\n        false, false, null, 10000, 1,\n        5, \"id\", new int[]{Vector.COMPRESS_LZ4, Vector.COMPRESS_LZ4, Vector.COMPRESS_DELTA});\nErrorCodeInfo ret;\n//MTW returns the error message\nret = multithreadedTableWriter_.insert(new Date(2022, 3, 23), random.nextInt() % 10000);\nif (!ret.errorInfo.equals(\"\"))\n    System.out.println(\"insert wrong format data: {3}\\n\" + ret.toString());\n```\n\nOutput:\n\n```\n\"\"\"\n    insert wrong format data: {3}\n      code=A2 info=Column counts don't match.  \n\"\"\"\n```\n\nIf error occurs when `MultithreadedTableWriter` is writing data, all working threads exit. If you continue to write data  to the server, an exception will be thrown and no data can be written as the thread has been terminated. You can use `getUnwrittenData()` to obtain the unwritten data and then rewrite it with `insertUnwrittenData()`. Please note that a new MTW object must be created to write the unwritten data.\n\nFor example:\n\n```java\nList\u003cList\u003cEntity\u003e\u003e unwriterdata = new ArrayList\u003c\u003e();\nunwriterdata = multithreadedTableWriter_.getUnwrittenData();\nSystem.out.println(\"{5} unwriterdata: \" + unwriterdata.size());\n//create a new object of MTW\nMultithreadedTableWriter newmultithreadedTableWriter = new MultithreadedTableWriter(HOST, PORT, \"admin\", \"123456\", \"dfs://valuedb3\", \"pdatetest\",\n        false, false, null, 10000, 1,\n        5, \"id\", new int[]{Vector.COMPRESS_LZ4, Vector.COMPRESS_LZ4, Vector.COMPRESS_DELTA});\ntry\n{\n    boolean writesuccess = true;\n    //insert the unwritten data\n    ret = newmultithreadedTableWriter.insertUnwrittenData(unwriterdata);\n}\nfinally\n{\n    newmultithreadedTableWriter.waitForThreadCompletion();\n    writeStatus = newmultithreadedTableWriter.getStatus();\n    System.out.println(\"writeStatus: {6}\\n\" + writeStatus.toString());\n}\n```\n\nOutput:\n\n```\n\"\"\"\n  {5} unwriterdata: 10 \n  writeStatus: {6}\n  errorCode     : \n  errorInfo     : \n  isExiting     : true\n  sentRows      : 10\n  unsentRows    : 0\n  sendFailedRows: 0\n  threadStatus  :\n          threadId        sentRows      unsentRows  sendFailedRows\n                23               3               0               0\n                24               2               0               0\n                25               1               0               0\n                26               3               0               0\n                27               1               0               0\n\"\"\"\n```\n\n## 8. Data Type Conversion\n\nJava API provides objects that correspond to DolphinDB data types. They are usually named as Basic+ \\\u003cDataType\\\u003e, such as BasicInt, BasicDate, etc.\n\nData Types\n\n| Java Primitive Data Type \t| Example \t| Java API Data Type \t| Example \t| DolphinDB Data Type \t| Example \t|\n|---\t|---\t|---\t|---\t|---\t|---\t|\n| Boolean \t| Boolean var = true; \t| BasicBoolean \t| BasicBoolean basicBoolean = new BasicBoolean(true); \t| BOOL \t| 1b, 0b, true, false \t|\n| Byte \t| byte number = 10; \t| BasicByte \t| BasicByte basicByte = new BasicByte((byte) 13); \t| CHAR \t| 'a', 97c \t|\n| LocalDate \t| LocalDate specificDate = LocalDate.of(2023, 6, 30); \t| BasicDate \t| BasicDate basicDate = new BasicDate(LocalDate.of(2021, 12, 9)); \t| DATE \t| 2023.06.13 \t|\n| Calendar \t| // create a Calendar object with specified date and time\u003cbr\u003eCalendar specificCalendar = Calendar.getInstance();\u003cbr\u003especificCalendar.set(2023, Calendar.JUNE, 30, 12, 0, 0); \t| BasicDate \t| BasicDate basicDate = new BasicDate(specificCalendar); \t| DATE \t| 2023.06.13 \t|\n|   \t| same as above \t| BasicDateHour \t| Calendar calendar = Calendar.getInstance();\u003cbr\u003ecalendar.set(2022,0,31,2,2,2);\u003cbr\u003eBasicDateHour date = new BasicDateHour(calendar); \t| DATEHOUR \t| 2012.06.13T13 \t|\n|   \t| same as above \t| BasicDateTime \t| BasicDateTime basicDateTime = new BasicDateTime(new GregorianCalendar()); \t| DATETIME \t| 2012.06.13 13:30:10 or 2012.06.13T13:30:10 \t|\n|   \t| same as above \t| BasicTime \t| BasicTime basicTime = new BasicTime(new GregorianCalendar()); \t| TIME \t| 13:30:10.008 \t|\n|   \t| same as above \t| BasicTimestamp \t| BasicTimestamp basicTimestamp = new BasicTimestamp(new GregorianCalendar()); \t| TIMESTAMP \t| 2012.06.13 13:30:10.008 or 2012.06.13T13:30:10.008 \t|\n| LocalDateTime \t| LocalDateTime currentDateTime = LocalDateTime.now(); \t| BasicDateHour \t| BasicDateHour basicDateHour = new BasicDateHour(LocalDateTime.now()); \t| DATEHOUR \t| 2012.06.13T13 \t|\n|   \t| same as above \t| BasicDateTime \t| BasicDateTime basicDateTime = new BasicDateTime(LocalDateTime.of(2000, 2, 2, 3, 2, 3, 2)); \t| DATETIME \t| 2012.06.13 13:30:10 or 2012.06.13T13:30:10 \t|\n|   \t| same as above \t| BasicMinute \t| BasicMinute basicMinute = new BasicMinute(LocalTime.of(11, 40, 53)); \t| MINUTE \t| 13:30m \t|\n|   \t| same as above \t| BasicNanoTime \t| BasicNanoTime basicNanoTime = new BasicNanoTime(LocalDateTime.of(2000, 2, 2, 3, 2, 3, 2)); \t| NANOTIME \t| 13:30:10.008007006 \t|\n|   \t| same as above \t| BasicNanoTimestamp \t| BasicNanoTimestamp bnts = new BasicNanoTimestamp(LocalDateTime.of(2018,11,12,8,1,1,123456789)); \t| NANOTIMESTAMP \t| 2012.06.13 13:30:10.008007006 or 2012.06.13T13:30:10.008007006 \t|\n|   \t| same as above \t| BasicTimestamp \t| BasicTimestamp basicTimestamp = new BasicTimestamp(LocalDateTime.of(2000, 2, 2, 3, 2, 3, 2)); \t| TIMESTAMP \t| 2012.06.13 13:30:10.008 or 2012.06.13T13:30:10.008 \t|\n| BigDecimal \t| BigDecimal decimal = new BigDecimal(\"3.1415926899\");\u003cbr\u003eBigDecimal afterSetScale = decimal.setScale(9, RoundingMode.FLOOR); \t| BasicDecimal32 \t| BasicDecimal32 basicDecimal32 = new BasicDecimal32(15645.00, 0); \t| DECIMAL32(S) \t| 3.1415926$DECIMAL32(3) \t|\n| BigDecimal \t| BigDecimal decimal = new BigDecimal(\"3.1234567891234567891\");BigDecimal afterSetScale = decimal.setScale(18, RoundingMode.FLOOR); \t| BasicDecimal64 \t| BasicDecimal64 decimal64 = new BasicDecimal64(15645.00, 0); \t| DECIMAL64(S) \t| 3.1415926$DECIMAL64(3), , 3.141P \t|\n| BigDecimal \t| BigDecimal decimal = new BigDecimal(\"3.123456789123456789123456789123456789123\");BigDecimal afterSetScale = decimal.setScale(38, RoundingMode.FLOOR); \t| BasicDecimal128 \t| BasicDecimal128 basicDecimal128 = new BasicDecimal128(\"15645.00\", 2); \t| DECIMAL128(S) \t|   \t|\n| Double \t| Double number = Double.valueOf(3.14); \t| BasicDouble \t| BasicDouble basicDouble = new BasicDouble(15.48); \t| DOUBLE \t| 15.48 \t|\n| - \t| - \t| BasicDuration \t| BasicDuration basicDuration = new BasicDuration(Entity.DURATION.SECOND, 1); \t| DURATION \t| 1s, 3M, 5y, 200ms \t|\n| Float \t| Float number = Float.valueOf(3.14f) \t| BasicFloat \t| BasicFloat basicFloat = new BasicFloat(2.1f); \t| FLOAT \t| 2.1f \t|\n| Integer \t| Integer number = 1; \t| BasicInt \t| BasicInt basicInt = new BasicInt(1); \t| INT \t| 1 \t|\n| - \t| - \t| BasicInt128 \t| BasicInt128 basicInt128 = BasicInt128.fromString(\"e1671797c52e15f763380b45e841ec32\"); \t| INT128 \t| e1671797c52e15f763380b45e841ec32 \t|\n| - \t| - \t| BasicIPAddr \t| BasicIPAddr basicIPAddr = BasicIPAddr.fromString(\"192.168.1.13\"); \t| IPADDR \t| 192.168.1.13 \t|\n| Long \t| Long number = 123456789L; \t| BasicLong \t| BasicLong basicLong = new BasicLong(367); \t| LONG \t| 367l \t|\n| YearMonth \t| YearMonth yearMonth = YearMonth.of(2023, 6); \t| BasicMonth \t| BasicMonth basicMonth = new BasicMonth(YearMonth.of(2022, 7)); \t| MONTH \t| 2012.06M \t|\n| LocalTime \t| LocalTime specificTime = LocalTime.of(10, 30, 0);  \t| BasicNanoTime \t| BasicNanoTime basicNanoTime = new BasicNanoTime(LocalTime.of(1, 1, 1, 1323433)); \t| NANOTIME \t| 13:30:10.008007006 \t|\n|   \t| same as above \t| BasicSecond \t| BasicSecond basicSecond = new BasicSecond(LocalTime.of(2, 2, 2)); \t| SECOND \t| 13:30:10 \t|\n|   \t| same as above \t| BasicMinute \t| BasicMinute basicMinute = new BasicMinute(new GregorianCalendar()); \t| MINUTE \t| 13:30m \t|\n|   \t| same as above \t| BasicTime \t| BasicTime basicTime = new BasicTime(LocalTime.of(13, 7, 55)); \t| TIME \t| 13:30:10.008 \t|\n| - \t| - \t| BasicPoint \t| BasicPoint basicPoint = new BasicPoint(6.4, 9.2); \t| POINT \t| (117.60972, 24.118418) \t|\n| short \t| short number = 100;  \t| BasicShort \t| BasicShort basicShort = new BasicShort((short) 21); \t| SHORT \t| 122h \t|\n| String \t| String s = \"abcd\"; \t| BasicString \t| BasicString basicString = new BasicString(\"colDefs\"); \t| STRING \t| \"Hello\" or 'Hello' or `Hello \t|\n| - \t| - \t| BasicString \t| BasicString basicString = new BasicString(\"Jmeter\", true); \t| BLOB \t| - \t|\n| UUID \t| UUID uuid = UUID.randomUUID(); \t| BasicUuid \t| BasicUuid.fromString(\"5d212a78-cc48-e3b1-4235-b4d91473ee87\") \t| UUID \t| 5d212a78-cc48-e3b1-4235-b4d91473ee87 \t|\n\nData forms\n\n|  \t|  \t|  \t|  \t|  \t|  \t|\n|---\t|---\t|---\t|---\t|---\t|---\t|\n| Set \t| - \t| BasicSet \t| BasicSet bs = new BasicSet(Entity.DATA_TYPE.DT_INT,4); \t| set \t| x=set(\\[5,5,3,4\\]);\u003cbr\u003ex; \t|\n| - \t| - \t| BasicDictionary \t| BasicDictionary bd = new BasicDictionary(Entity.DATA_TYPE.DT_STRING, Entity.DATA_TYPE.DT_DATETIME,2); \t| DICTIONARY \t| x=1 2 3 1;\u003cbr\u003ey=2.3 4.6 5.3 6.4;\u003cbr\u003ez=dict(x, y); \t|\n\nThe majority of DolphinDB data types can be constructed from corresponding Java data types. For examples, INT in DolphinDB from 'new BasicInt(4)', DOUBLE in DolphinDB from 'new BasicDouble(1.23)'. The following DolphinDB data types, however, need to be constructed in different ways:\n\n- CHAR type: as the CHAR type in DolphinDB is stored as a byte, we can use the BasicByte type to construct CHAR in Java API, for example 'new BasicByte((byte)'c')'.\n- SYMBOL type: the SYMBOL type in DolphinDB is stored as INT to improve the efficiency of storage and query of STRING vectors (especially for vectors containing multiple duplicate strings). Java API does not provide BasicSymbol object. It uses BasicString to deal with strings. The Java API has provided the BasicSymbolVector type for handling STRING vectors since version 1.30.17.1. Note that when downloading data to Java, it is recommended to use AbstractVector and the `getString` method to access downloaded SYMBOL data, instead of casting to BasicSymbolVector or BasicStringVector.\n- Temporal types: temporal data types are stored as INT or LONG in DolphinDB. DolphinDB provides 9 temporal data types: date, month, time, minute, second, datetime, timestamp, nanotime and nanotimestamp. For detailed description, please refer to [DolphinDB Temporal Type and Conversion](https://www.dolphindb.com/help/DataManipulation/TemporalObjects/TemporalTypeandConversion.html). Since Java also provides data types such as LocalDate, LocalTime, LocalDateTime and YearMonth, Java API provides conversion functions in the `Utils` class between all Java temporal types and INT or LONG.\n\nThe following script shows the correspondence between DolphinDB temporal types and Java primitive temporal types:\n\n```java\n//Date:2018.11.12\nBasicDate bd = new BasicDate(LocalDate.of(2018,11,12));\n\n//Month:2018.11M\nBasicMonth bm = new BasicMonth(YearMonth.of(2018,11));\n\n//Time:20:08:01.123\nBasicTime bt = new BasicTime(LocalTime.of(20,8,1,123000000));\n\n//Minute:20:08m\nBasicMinute bmn = new BasicMinute(LocalTime.of(20,8));\n\n//Second:20:08:01\nBasicSecond bs = new BasicSecond(LocalTime.of(20,8,1));\n\n//DateTime: 2018.11.12T08:01:01\nBasicDateTime bdt = new BasicDateTime(LocalDateTime.of(2018,11,12,8,1,1));\n\n//Timestamp: 2018.11.12T08:01:01.123\nBasicTimestamp bts = new BasicTimestamp(LocalDateTime.of(2018,11,12,8,1,1,123000000));\n\n//NanoTime: 20:08:01.123456789\nBasicNanoTime bnt = new BasicNanoTime(LocalTime.of(20,8,1,123456789));\n\n//NanoTimestamp: 2018.11.12T20:08:01.123456789\nBasicNanoTimestamp bnts = new BasicNanoTimestamp(LocalDateTime.of(2018,11,12,8,1,1,123456789));\n```\n\nIf a temporal variable is stored as timestamp in a third-party system, DolphinDB temporal object can also be instantiated with a timestamp. The `Utils` class in the Java API provides conversion algorithms for various temporal types and standard timestamps, such as converting millisecond timestamps to DolphinDB's BasicTimestamp objects:\n\n```\nLocalDateTime dt = Utils.parseTimestamp(1543494854000l);\nBasicTimestamp ts = new BasicTimestamp(dt);\n```\n\nWe can also convert a DolphinDB object to a timestamp of an integer or long integer. For examples:\n\n```\nLocalDateTime dt = ts.getTimestamp();\nlong timestamp = Utils.countMilliseconds(dt);\n```\n\nThe `Utils` class provides the following methods to handle a variety of timestamp precisions:\n\n- Utils.countMonths: calculate the monthly difference between a given time and 1970.01, returning INT.\n- Utils.countDays: calculate the difference in the number of days between the given time and 1970.01.01, returning INT.\n- Utils.countMinutes: calculate the minute difference between the given time and 1970.01.01T00:00, returning INT.\n- Utils.countSeconds: calculate the difference in seconds between a given time and 1970.01.01T00:00:00, returning INT.\n- Utils.countMilliseconds: calculate the difference in milliseconds between a given time and 1970.01.01T00:00:00, returning LONG.\n- Utils.countNanoseconds: calculate the difference in nanoseconds between a given time and 1970.01.01T00:00:00.000, returning LONG.\n\n\n## 9. Java Streaming API\n\nA Java program can subscribe to streaming data via API. Java API can acquire streaming data in the following 3 ways: ThreadedClient, ThreadPooledClient, and PollingClient.\n\n### 9.1. Interfaces\n\nThe corresponding interfaces of `subscribe` are:\n\n(1) Subscribe using ThreadedClient:\n\n```java\nsubscribe(string host, int port, string tableName, string actionName, MessageHandler handler, long offset, bool reconnect, IVector filter, int batchSize, float throttle = 0.01f, StreamDeserializer deserializer = null, string user = \"\", string password = \"\")\n```\n\n**Parameters:**\n\n- **host:** the IP address of the publisher node.\n- **port:** the port number of the publisher node.\n- **tableName:** a string indicating the name of the publishing stream table.\n- **actionName:** a string indicating the name of the subscription task.\n- **handler:** a user-defined function to process the subscribed data.\n- **offset:** an integer indicating the position of the first message where the subscription begins. A message is a row of the stream table. If *offset* is unspecified, negative or exceeding the number of rows in the stream table, the subscription starts with the next new message. *offset* is relative to the first row of the stream table when it is created. If some rows were cleared from memory due to cache size limit, they are still considered in determining where the subscription starts.\n- **reconnect:** a Boolean value indicating whether to resubscribe after network disconnection.\n- **filter:** a vector indicating the filtering conditions. Only the rows with values of the filtering column in the vector specified by the parameter *filter* are published to the subscriber.\n- **batchSize:** an integer indicating the number of unprocessed messages to trigger the *handler*. If it is positive, the *handler* does not process messages until the number of unprocessed messages reaches *batchSize*. If it is unspecified or non-positive, the *handler* processes incoming messages as soon as they come in.\n- **throttle:** an integer indicating the maximum waiting time (in seconds) before the *handler* processes the incoming messages. The default value is 1. This optional parameter has no effect if *batchSize* is not specified.\n- **deserializer:** the deserializer for the subscribed heterogeneous stream table.\n- **user:** a string indicating the username used to connect to the server\n- **password:** a string indicating the password used to connect to the server\n\n(2) Subscribe using ThreadPooledClient:\n\n```java\nsubscribe(string host, int port, string tableName, string actionName, MessageHandler handler, long offset, bool reconnect, IVector filter, StreamDeserializer deserializer = null, string user = \"\", string password = \"\")\n```\n\n(3) Subscribe using PollingClient:\n\n```java\nsubscribe(string host, int port, string tableName, string actionName, long offset, bool reconnect, IVector filter, StreamDeserializer deserializer = null, string user = \"\", string password = \"\")\n```\n\n### 9.2. Code Examples\n\nThe following example introduces how to subscribe to stream table:\n\n- The application on the client periodically checks if new data has been added to the streaming table. If yes, the application will acquire and consume the new data.\n\n```java\nPollingClient client = new PollingClient(subscribePort);\nTopicPoller poller1 = client.subscribe(serverIP, serverPort, tableName, offset);\n\nwhile (true) {\n   ArrayList\u003cIMessage\u003e msgs = poller1.poll(1000);\n   if (msgs.size() \u003e 0) {\n         BasicInt value = msgs.get(0).getEntity(2);  //access the third cell of the first row\n   }\n} \n```\n\nAfter poller1 detects that new data is added to the streaming table, it will pull the new data. When there is no new data, the Java program is waiting at poller1.poll method.\n\n- The API uses MessageHandler to get new data\n\nFirst we need to define the message handler, which needs to implement com.xxdb.streaming.client.MessageHandle interface.\n\n```java\npublic class MyHandler implements MessageHandler {\n       public void doEvent(IMessage msg) {\n               BasicInt qty = msg.getValue(2);\n               //..data processing\n       }\n}\n```\n\nYou can pass the handler instance into function `subscribe` as a parameter with single-thread or multi-thread callbacks.\n\n(1) ThreadedClient\n\n```java\nThreadedClient client = new ThreadedClient(subscribePort);\nclient.subscribe(serverIP, serverPort, tableName, new MyHandler(), offsetInt);\n```\n\nWhen new data is added to the streaming table, the system notifies Java API to use 'MyHandler' method to acquire the new data.\n\n(2) ThreadPooledClient\n\n```java\nThreadPooledClient client = new ThreadPooledClient(10000,10);\nclient.subscribe(serverIP, serverPort, tableName, new MyHandler(), offsetInt);\n```\n\n### 9.3. Reconnect\n\nParameter *reconnect* is a Boolean value indicating whether to automatically resubscribe after the subscription experiences an unexpected interruption. The default value is false.\n\nIf *reconnect*=true, whether and how the system resumes the subscription depends on how the unexpected interruption of subscription is caused.\n\n- If the publisher and the subscriber both stay on but the network connection is interrupted, then after network is restored, the subscriber resumes subscription from where the network interruption occurs.\n- If the publisher crashes, the subscriber will keep attempting to resume subscription after the publisher restarts.\n  - If persistence was enabled on the publisher, the publisher starts to read the persisted data on disk after restarting. The subscriber can't successfully resubscribe automatically until the publisher has read the data for the time when the publisher crashed.\n  - If persistence was not enabled on the publisher, the subscriber will fail to automatically resubscribe.\n- If the subscriber crashes, the subscriber won't automatically resume the subscription after it restarts. In this case, we need to execute function `subscribe` again.\n\nParameter 'reconnect' is set to be true for the following example：\n\n```java\nPollingClient client = new PollingClient(subscribePort);\nTopicPoller poller1 = client.subscribe(serverIP, serverPort, tableName, offset, true);\n```\n\n### 9.4. Filter\n\nParameter *filter* is a vector. It is used together with function `setStreamTableFilterColumn` at the publisher node. Function `setStreamTableFilterColumn` specifies the filtering column in the streaming table. Only the rows with filtering column values in *filter* are published.\n\nIn the following example, parameter *filter* is assigned an INT vector \\[1,2\\]:\n\n```java\nBasicIntVector filter = new BasicIntVector(2);\nfilter.setInt(0, 1);\nfilter.setInt(1, 2);\n\nPollingClient client = new PollingClient(subscribePort);\nTopicPoller poller1 = client.subscribe(serverIP, serverPort, tableName, actionName, offset, filter);\n```\n\n### 9.5. Subscribe to a Heterogeneous Table\n\nSince DolphinDB server version 1.30.17/2.00.5, the [replay](https://dolphindb.com/help/FunctionsandCommands/FunctionReferences/r/replay.html) function supports replaying (serializing) multiple stream tables with different schemata into a single stream table (known as \"heterogeneous stream table\"). Starting from DolphinDB Java API version 1.30.19, a new class `streamDeserializer` has been introduced for the subscription and deserialization of heterogeneous stream table.\n\n#### 9.5.1. Construct Deserializer for Heterogeneous Stream Table\n\nYou can construct a deserializer for heterogeneous table with `streamDeserializer`.\n\n**(1) With specified table schema:**\n\n- specified schema\n\n```java\nStreamDeserializer(Map\u003cString, BasicDictionary\u003e filters)\n```\n\n- specified column types\n\n```java\nStreamDeserializer(HashMap\u003cString, List\u003cEntity.DATA_TYPE\u003e\u003e filters)\n```\n\n**(2) With specified table:**\n\n```java\nStreamDeserializer(Map\u003cString, Pair\u003cString, String\u003e\u003e tableNames, DBConnection conn)\n```\n\nCode example:\n\n```java\n//Supposing the inputTables to be replayed is:\n//d = dict(['msg1', 'msg2'], [table1, table2]); \\\n//replay(inputTables = d, outputTables = `outTables, dateColumn = `timestampv, timeColumn = `timestampv)\";\n\n//create a deserializer for heterogeneous table \n\n{//specify schema\n    BasicDictionary table1_schema = (BasicDictionary)conn.run(\"table1.schema()\");\n    BasicDictionary table2_schema = (BasicDictionary)conn.run(\"table2.schema()\");\n    Map\u003cString,BasicDictionary \u003e tables = new HashMap\u003c\u003e();\n    tables.put(\"msg1\", table1_schema);\n    tables.put(\"msg2\", table2_schema);\n    StreamDeserializer streamFilter = new StreamDeserializer(tables);\n}\n{//or specify column types\n    Entity.DATA_TYPE[] array1 = {DT_DATETIME,DT_TIMESTAMP,DT_SYMBOL,DT_DOUBLE,DT_DOUBLE};\n    Entity.DATA_TYPE[] array2 = {DT_DATETIME,DT_TIMESTAMP,DT_SYMBOL,DT_DOUBLE};\n    List\u003cEntity.DATA_TYPE\u003e filter1 = new ArrayList\u003c\u003e(Arrays.asList(array1));\n    List\u003cEntity.DATA_TYPE\u003e filter2 = new ArrayList\u003c\u003e(Arrays.asList(array2));\n    HashMap\u003cString, List\u003cEntity.DATA_TYPE\u003e\u003e filter = new HashMap\u003c\u003e();\n    filter.put(\"msg1\",filter1);\n    filter.put(\"msg2\",filter2);\n    StreamDeserializer streamFilter = new StreamDeserializer(filter);\n}\n{//specify tables\n    Map\u003cString, Pair\u003cString, String\u003e\u003e tables = new HashMap\u003c\u003e();\n    tables.put(\"msg1\", new Pair\u003c\u003e(\"\", \"table1\"));\n    tables.put(\"msg2\", new Pair\u003c\u003e(\"\", \"table2\"));\n    //conn is an optional parameter\n    StreamDeserializer streamFilter = new StreamDeserializer(tables, conn);\n}\n```\n\n#### 9.5.2. Subscribe to a Heterogeneous Table\n\n(1) subscribe to a heterogeneous table using ThreadedClient:\n\n- you can specify the parameter *deserializer* of function `subscribe` so as to deserialize the table when data is ingested:\n\n```java\nThreadedClient client = new ThreadedClient(8676);\nclient.subscribe(hostName, port, tableName, actionName, handler, 0, true, null, streamFilter, false);\n```\n\n- you can also add the streamFilter to user-defined Handler:\n\n```java\nclass Handler6 implements MessageHandler {\n    private StreamDeserializer deserializer_;\n    private List\u003cBasicMessage\u003e msg1 = new ArrayList\u003c\u003e();\n    private List\u003cBasicMessage\u003e msg2 = new ArrayList\u003c\u003e();\n\n    public Handler6(StreamDeserializer deserializer) {\n        deserializer_ = deserializer;\n    }\n    public void batchHandler(List\u003cIMessage\u003e msgs) {\n    }\n\n    public void doEvent(IMessage msg) {\n        try {\n                BasicMessage message = deserializer_.parse(msg);\n                if (message.getSym().equals(\"msg1\")) {\n                    msg1.add(message);\n                } else if (message.getSym().equals(\"msg2\")) {\n                    msg2.add(message);\n                }\n        } catch (Exception e) {\n                e.printStackTrace();\n        }\n    }\n\n    public List\u003cBasicMessage\u003e getMsg1() {\n        return msg1;\n    }\n    public List\u003cBasicMessage\u003e getMsg2() {\n        return msg2;\n    }\n}\n\nThreadedClient client = new ThreadedClient(listenport);\nHandler6 handler = new Handler6(streamFilter);\nclient.subscribe(hostName, port, tableName, actionName, handler, 0, true);\n```\n\n(2) subscribing to a heterogeneous table using ThreadPooledClient is similar as above:\n\n- specify the parameter *deserializer* of function `subscribe`\n\n```java\nHandler6 handler = new Handler6(streamFilter);\nThreadPooledClient client1 = new ThreadPooledClient(listenport, threadCount);\nclient.subscribe(hostName, port, tableName, actionName, handler, 0, true);\n```\n\n- add the streamFilter to user-defined Handler:\n\n```java\nThreadPooledClient client = new ThreadPooledClient(listenport, threadCount);\nclient.subscribe(hostName, port, tableName, actionName, handler, 0, true, null, streamFilter, false);\n```\n\n(3) As PollingClient does not support callbacks, you can only pass the *deserializer* parameter to the function `subscribe`:\n\n```java\nPollingClient client = new PollingClient(listenport);\nTopicPoller poller = subscribe(hostName, port, tableName, actionName, 0, true, null, streamFilter);\n```\n\n### 9.6. Unsubscribe\n\nEach subscription is identified with a subscription topic. Subscription fails if a topic with the same name already exists. You can cancel the subscription with `unsubscribe`.\n\n```java\nclient.unsubscribe(serverIP, serverPort, tableName,actionName);\n```\n\n \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdolphindb%2Fapi-java","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdolphindb%2Fapi-java","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdolphindb%2Fapi-java/lists"}