六月婷婷AV,国产偷窥猎奇福利二区,日韩三级片。,好吊色网站,日韩成人中文在线视频,国产亚洲午夜啪啪,亚洲欧美另类国产精品,国产成人av1,任你艹在线观看

Java Connector

總體介紹

taos-jdbcdriver 的實現(xiàn)包括 2 種形式: JDBC-JNI 和 JDBC-RESTful(taos-jdbcdriver-2.0.18 開始支持 JDBC-RESTful)。 JDBC-JNI 通過調(diào)用客戶端 libtaos.so(或 taos.dll )的本地方法實現(xiàn), JDBC-RESTful 則在內(nèi)部封裝了 RESTful 接口實現(xiàn)。

tdengine-connector

上圖顯示了 3 種 Java 應(yīng)用使用連接器訪問 TDengine 的方式:

  • JDBC-JNI:Java 應(yīng)用在物理節(jié)點1(pnode1)上使用 JDBC-JNI 的 API ,直接調(diào)用客戶端 API(libtaos.so 或 taos.dll)將寫入和查詢請求發(fā)送到位于物理節(jié)點2(pnode2)上的 taosd 實例。
  • RESTful:應(yīng)用將 SQL 發(fā)送給位于物理節(jié)點2(pnode2)上的 RESTful 連接器,再調(diào)用客戶端 API(libtaos.so)。
  • JDBC-RESTful:Java 應(yīng)用通過 JDBC-RESTful 的 API ,將 SQL 封裝成一個 RESTful 請求,發(fā)送給物理節(jié)點2的 RESTful 連接器。

TDengine 的 JDBC 驅(qū)動實現(xiàn)盡可能與關(guān)系型數(shù)據(jù)庫驅(qū)動保持一致,但TDengine與關(guān)系對象型數(shù)據(jù)庫的使用場景和技術(shù)特征存在差異,導致 taos-jdbcdriver 與傳統(tǒng)的 JDBC driver 也存在一定差異。在使用時需要注意以下幾點:

  • TDengine 目前不支持針對單條數(shù)據(jù)記錄的刪除操作。
  • 目前不支持事務(wù)操作。

JDBC-JNI和JDBC-RESTful的對比

對比項JDBC-JNIJDBC-RESTful
支持的操作系統(tǒng) Linux、Windows 全平臺
是否需要安裝 client 需要 不需要
server 升級后是否需要升級 client 需要 不需要
寫入性能 JDBC-RESTful 是 JDBC-JNI 的 50%~90%
查詢性能 JDBC-RESTful 與 JDBC-JNI 沒有差別

注意:

  • 與 JNI 方式不同,RESTful 接口是無狀態(tài)的。在使用JDBC-RESTful時,需要在sql中指定表、超級表的數(shù)據(jù)庫名稱。例如:
    INSERT INTO test.t1 USING test.weather (ts, temperature) TAGS('beijing') VALUES(now, 24.6);
  • 從taos-jdbcdriver-2.0.36和TDengine 2.2.0.0 版本開始,如果在url中指定了dbname,那么,JDBC-RESTful會默認使用/rest/sql/dbname作為 restful 請求的 url,在 SQL 中不需要指定dbname。例如:url為jdbc:TAOS-RS://127.0.0.1:6041/test,那么,可以執(zhí)行sql:insert into t1 using weather(ts, temperature) tags('beijing') values(now, 24.6);

TAOS-JDBCDriver 版本以及支持的 TDengine 版本和 JDK 版本

taos-jdbcdriver 版本 TDengine 2.0.x.x 版本 TDengine 2.2.x.x 版本 TDengine 2.4.x.x 版本 JDK 版本
2.0.38 X X 2.4.0.14 及以上 1.8.x
2.0.37 X X 2.4.0.6 及以上 1.8.x
2.0.36 X 2.2.2.11 及以上 2.4.0.0 - 2.4.0.5 1.8.x
2.0.35 X 2.2.2.11 及以上 2.3.0.0 - 2.4.0.5 1.8.x
2.0.33 - 2.0.34 2.0.3.0 及以上 2.2.0.0 及以上 2.4.0.0 - 2.4.0.5 1.8.x
2.0.31 - 2.0.32 2.1.3.0 - 2.1.7.7 X X 1.8.x
2.0.22 - 2.0.30 2.0.18.0 - 2.1.2.1 X X 1.8.x
2.0.12 - 2.0.21 2.0.8.0 - 2.0.17.4 X X 1.8.x
2.0.4 - 2.0.11 2.0.0.0 - 2.0.7.3 X X 1.8.x

TDengine DataType 和 Java DataType

TDengine 目前支持時間戳、數(shù)字、字符、布爾類型,與 Java 對應(yīng)類型轉(zhuǎn)換如下:

TDengine DataType JDBCType (driver 版本 < 2.0.24) JDBCType (driver 版本 >= 2.0.24)
TIMESTAMP java.lang.Long java.sql.Timestamp
INT java.lang.Integer java.lang.Integer
BIGINT java.lang.Long java.lang.Long
FLOAT java.lang.Float java.lang.Float
DOUBLE java.lang.Double java.lang.Double
SMALLINT java.lang.Short java.lang.Short
TINYINT java.lang.Byte java.lang.Byte
BOOL java.lang.Boolean java.lang.Boolean
BINARY java.lang.String byte array
NCHAR java.lang.String java.lang.String
JSON - java.lang.String

注意:JSON類型僅在tag中支持。

安裝Java Connector

安裝前準備

使用Java Connector連接數(shù)據(jù)庫前,需要具備以下條件:

  1. Linux或Windows操作系統(tǒng)
  2. Java 1.8以上運行時環(huán)境
  3. TDengine-client(使用JDBC-JNI時必須,使用JDBC-RESTful時非必須)

注意:由于 TDengine 的應(yīng)用驅(qū)動是使用C語言開發(fā)的,使用 taos-jdbcdriver 驅(qū)動包時需要依賴系統(tǒng)對應(yīng)的本地函數(shù)庫。

  • libtaos.so 在 Linux 系統(tǒng)中成功安裝 TDengine 后,依賴的本地函數(shù)庫 libtaos.so 文件會被自動拷貝至 /usr/lib/libtaos.so,該目錄包含在 Linux 自動掃描路徑上,無需單獨指定。
  • taos.dll 在 Windows 系統(tǒng)中安裝完客戶端之后,驅(qū)動包依賴的 taos.dll 文件會自動拷貝到系統(tǒng)默認搜索路徑 C:/Windows/System32 下,同樣無需要單獨指定。

注意:在 Windows 環(huán)境開發(fā)時需要安裝 TDengine 對應(yīng)的 windows 客戶端,Linux 服務(wù)器安裝完 TDengine 之后默認已安裝 client,也可以單獨安裝 Linux 客戶端 連接遠程 TDengine Server。

通過maven獲取JDBC driver

目前 taos-jdbcdriver 已經(jīng)發(fā)布到 Sonatype Repository 倉庫,且各大倉庫都已同步。

maven 項目中,在pom.xml 中添加以下依賴:

<dependency>
 <groupId>com.taosdata.jdbc</groupId>
 <artifactId>taos-jdbcdriver</artifactId>
 <!--具體版本請參考上面的版本對應(yīng)表-->
 <version>2.x.xx</version>
</dependency>

通過源碼編譯獲取JDBC driver

可以通過下載TDengine的源碼,自己編譯最新版本的java connector

git clone https://github.com/taosdata/TDengine.git
cd TDengine/src/connector/jdbc
mvn clean package -Dmaven.test.skip=true

編譯后,在target目錄下會產(chǎn)生taos-jdbcdriver-2.0.XX-dist.jar的jar包。

Java連接器的使用

獲取連接

指定URL獲取連接

通過指定URL獲取連接,如下所示:

Class.forName("com.taosdata.jdbc.rs.RestfulDriver");
String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/test?user=root&password=taosdata";
Connection conn = DriverManager.getConnection(jdbcUrl);

以上示例,使用 JDBC-RESTful 的 driver,建立了到 hostname 為 taosdemo.com,端口為 6041,數(shù)據(jù)庫名為 test 的連接。這個 URL 中指定用戶名(user)為 root,密碼(password)為 taosdata。

使用 JDBC-RESTful 接口,不需要依賴本地函數(shù)庫。與 JDBC-JNI 相比,僅需要:

  1. driverClass 指定為“com.taosdata.jdbc.rs.RestfulDriver”;
  2. jdbcUrl 以“jdbc:TAOS-RS://”開頭;
  3. 使用 6041 作為連接端口。

從 taos-jdbcdriver-2.0.38 和 TDengine 2.4.0.12 版本開始,JDBC-RESTful 的 driver 增加批量拉取數(shù)據(jù)功能。taos-jdbcdriver 與 TDengine 之間通過 WebSocket 連接進行數(shù)據(jù)傳輸。相較于 HTTP,WebSocket 可以使 JDBC-RESTful 支持大數(shù)據(jù)量查詢,并提升查詢性能。

連接開啟批量拉取方式:

String url = "jdbc:TAOS-RS://taosdemo.com:6041/?user=root&password=taosdata";Properties properties = new Properties();
properties.setProperty(TSDBDriver.PROPERTY_KEY_BATCH_LOAD, "true");
Connection connection = DriverManager.getConnection(url, properties);

如果希望獲得更好的寫入和查詢性能,Java 應(yīng)用可以使用 JDBC-JNI 的 driver,如下所示:

Class.forName("com.taosdata.jdbc.TSDBDriver");
String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/test?user=root&password=taosdata";
Connection conn = DriverManager.getConnection(jdbcUrl);

以上示例,使用了 JDBC-JNI 的 driver,建立了到 hostname 為 taosdemo.com,端口為 6030(TDengine 的默認端口),數(shù)據(jù)庫名為 test 的連接。這個 URL 中指定用戶名(user)為 root,密碼(password)為 taosdata。

注意:使用 JDBC-JNI 的 driver,taos-jdbcdriver 驅(qū)動包時需要依賴系統(tǒng)對應(yīng)的本地函數(shù)庫(Linux 下是 libtaos.so;Windows 下是 taos.dll)。

在 Windows 環(huán)境開發(fā)時需要安裝 TDengine 對應(yīng)的 windows 客戶端,Linux 服務(wù)器安裝完 TDengine 之后默認已安裝 client,也可以單獨安裝 Linux 客戶端 連接遠程 TDengine Server。

JDBC-JNI 的使用請參見視頻教程。

TDengine 的 JDBC URL 規(guī)范格式為: jdbc:[TAOS|TAOS-RS]://[host_name]:[port]/[database_name]?[user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}]

url中的配置參數(shù)如下:

  • user:登錄 TDengine 用戶名,默認值 'root'。
  • password:用戶登錄密碼,默認值 'taosdata'。
  • cfgdir:客戶端配置文件目錄路徑,Linux OS 上默認值 /etc/taos,Windows OS 上默認值 C:/TDengine/cfg
  • charset:客戶端使用的字符集,默認值為系統(tǒng)字符集。
  • locale:客戶端語言環(huán)境,默認值系統(tǒng)當前 locale。
  • timezone:客戶端使用的時區(qū),默認值為系統(tǒng)當前時區(qū)。
  • batchfetch: 僅在使用JDBC-JNI時生效。true:在執(zhí)行查詢時批量拉取結(jié)果集;false:逐行拉取結(jié)果集。默認值為:false。
  • timestampFormat: 僅在使用JDBC-RESTful時生效. 'TIMESTAMP':結(jié)果集中timestamp類型的字段為一個long值; 'UTC':結(jié)果集中timestamp類型的字段為一個UTC時間格式的字符串; 'STRING':結(jié)果集中timestamp類型的字段為一個本地時間格式的字符串。默認值為'STRING'。
  • batchErrorIgnore:true:在執(zhí)行Statement的executeBatch時,如果中間有一條sql執(zhí)行失敗,繼續(xù)執(zhí)行下面的sql了。false:不再執(zhí)行失敗sql后的任何語句。默認值為:false。

指定URL和Properties獲取連接

除了通過指定的 URL 獲取連接,還可以使用 Properties 指定建立連接時的參數(shù),如下所示:

public Connection getConn() throws Exception{
  Class.forName("com.taosdata.jdbc.TSDBDriver");
  // Class.forName("com.taosdata.jdbc.rs.RestfulDriver");
  String jdbcUrl = "jdbc:TAOS://taosdemo.com:6030/test?user=root&password=taosdata";
  // String jdbcUrl = "jdbc:TAOS-RS://taosdemo.com:6041/test?user=root&password=taosdata";
  Properties connProps = new Properties();
  connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
  connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");
  connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
  Connection conn = DriverManager.getConnection(jdbcUrl, connProps);
  return conn;
}

以上示例,建立一個到 hostname 為 taosdemo.com,端口為 6030,數(shù)據(jù)庫名為 test 的連接。注釋為使用 JDBC-RESTful 時的方法。這個連接在 url 中指定了用戶名(user)為 root,密碼(password)為 taosdata,并在 connProps 中指定了使用的字符集、語言環(huán)境、時區(qū)等信息。

properties 中的配置參數(shù)如下:

  • TSDBDriver.PROPERTY_KEY_USER:登錄 TDengine 用戶名,默認值 'root'。
  • TSDBDriver.PROPERTY_KEY_PASSWORD:用戶登錄密碼,默認值 'taosdata'。
  • TSDBDriver.PROPERTY_KEY_CONFIG_DIR:客戶端配置文件目錄路徑,Linux OS 上默認值 /etc/taos,Windows OS 上默認值 C:/TDengine/cfg。
  • TSDBDriver.PROPERTY_KEY_CHARSET:客戶端使用的字符集,默認值為系統(tǒng)字符集。
  • TSDBDriver.PROPERTY_KEY_LOCALE:客戶端語言環(huán)境,默認值系統(tǒng)當前 locale。
  • TSDBDriver.PROPERTY_KEY_TIME_ZONE:客戶端使用的時區(qū),默認值為系統(tǒng)當前時區(qū)。
  • TSDBDriver.PROPERTY_KEY_BATCH_LOAD: true:在執(zhí)行查詢時批量拉取結(jié)果集;false:逐行拉取結(jié)果集。默認值為:false。
  • TSDBDriver.PROPERTY_KEY_TIMESTAMP_FORMAT: 僅在使用JDBC-RESTful時生效. 'TIMESTAMP':結(jié)果集中timestamp類型的字段為一個long值; 'UTC':結(jié)果集中timestamp類型的字段為一個UTC時間格式的字符串; 'STRING':結(jié)果集中timestamp類型的字段為一個本地時間格式的字符串。默認值為'STRING'。
  • TSDBDriver.PROPERTY_KEY_BATCH_ERROR_IGNORE:true:在執(zhí)行Statement的executeBatch時,如果中間有一條sql執(zhí)行失敗,繼續(xù)執(zhí)行下面的sq了。false:不再執(zhí)行失敗sql后的任何語句。默認值為:false。

使用客戶端配置文件建立連接

當使用 JDBC-JNI 連接 TDengine 集群時,可以使用客戶端配置文件,在客戶端配置文件中指定集群的 firstEp、secondEp參數(shù)。如下所示:

  1. 在 Java 應(yīng)用中不指定 hostname 和 port
public Connection getConn() throws Exception{
  Class.forName("com.taosdata.jdbc.TSDBDriver");
  String jdbcUrl = "jdbc:TAOS://:/test?user=root&password=taosdata";
  Properties connProps = new Properties();
  connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
  connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");
  connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
  Connection conn = DriverManager.getConnection(jdbcUrl, connProps);
  return conn;
}
  1. 在配置文件中指定 firstEp 和 secondEp
# first fully qualified domain name (FQDN) for TDengine system
firstEp               cluster_node1:6030

# second fully qualified domain name (FQDN) for TDengine system, for cluster only
secondEp              cluster_node2:6030

# default system charset
# charset               UTF-8  

# system locale
# locale                en_US.UTF-8

以上示例,jdbc 會使用客戶端的配置文件,建立到 hostname 為 cluster_node1、端口為 6030、數(shù)據(jù)庫名為 test 的連接。當集群中 firstEp 節(jié)點失效時,JDBC 會嘗試使用 secondEp 連接集群。

TDengine 中,只要保證 firstEp 和 secondEp 中一個節(jié)點有效,就可以正常建立到集群的連接。

注意:這里的配置文件指的是調(diào)用 JDBC Connector 的應(yīng)用程序所在機器上的配置文件,Linux OS 上默認值 /etc/taos/taos.cfg ,Windows OS 上默認值 C://TDengine/cfg/taos.cfg。

配置參數(shù)的優(yōu)先級

通過以上 3 種方式獲取連接,如果配置參數(shù)在 url、Properties、客戶端配置文件中有重復,則參數(shù)的優(yōu)先級由高到低分別如下:

  1. JDBC URL 參數(shù),如上所述,可以在 JDBC URL 的參數(shù)中指定。
  2. Properties connProps
  3. 客戶端配置文件 taos.cfg

例如:在 url 中指定了 password 為 taosdata,在 Properties 中指定了 password 為 taosdemo,那么,JDBC 會使用 url 中的 password 建立連接。

更多詳細配置請參考客戶端配置

創(chuàng)建數(shù)據(jù)庫和表

Statement stmt = conn.createStatement();

// create database
stmt.executeUpdate("create database if not exists db");

// use database
stmt.executeUpdate("use db");

// create table
stmt.executeUpdate("create table if not exists tb (ts timestamp, temperature int, humidity float)");

注意:如果不使用 use db 指定數(shù)據(jù)庫,則后續(xù)對表的操作都需要增加數(shù)據(jù)庫名稱作為前綴,如 db.tb。

插入數(shù)據(jù)

// insert data
int affectedRows = stmt.executeUpdate("insert into tb values(now, 23, 10.3) (now + 1s, 20, 9.3)");

System.out.println("insert " + affectedRows + " rows.");

now 為系統(tǒng)內(nèi)部函數(shù),默認為客戶端所在計算機當前時間。 now + 1s 代表客戶端當前時間往后加 1 秒,數(shù)字后面代表時間單位:a(毫秒),s(秒),m(分),h(小時),d(天),w(周),n(月),y(年)。

查詢數(shù)據(jù)

// query data
ResultSet resultSet = stmt.executeQuery("select * from tb");

Timestamp ts = null;
int temperature = 0;
float humidity = 0;
while(resultSet.next()){

    ts = resultSet.getTimestamp(1);
    temperature = resultSet.getInt(2);
    humidity = resultSet.getFloat("humidity");

    System.out.printf("%s, %d, %s\n", ts, temperature, humidity);
}

查詢和操作關(guān)系型數(shù)據(jù)庫一致,使用下標獲取返回字段內(nèi)容時從 1 開始,建議使用字段名稱獲取。

處理異常

在報錯后,通過SQLException可以獲取到錯誤的信息和錯誤碼:

try (Statement statement = connection.createStatement()) {
    // executeQuery
    ResultSet resultSet = statement.executeQuery(sql);
    // print result
    printResult(resultSet);
} catch (SQLException e) {
    System.out.println("ERROR Message: " + e.getMessage());
    System.out.println("ERROR Code: " + e.getErrorCode());
    e.printStackTrace();
}

JDBC連接器可能報錯的錯誤碼包括3種:JDBC driver本身的報錯(錯誤碼在0x2301到0x2350之間),JNI方法的報錯(錯誤碼在0x2351到0x2400之間),TDengine其他功能模塊的報錯。

具體的錯誤碼請參考:

  • https://github.com/taosdata/TDengine/blob/develop/src/connector/jdbc/src/main/java/com/taosdata/jdbc/TSDBErrorNumbers.java
  • https://github.com/taosdata/TDengine/blob/develop/src/inc/taoserror.h

通過參數(shù)綁定寫入數(shù)據(jù)

從 2.1.2.0 版本開始,TDengine 的 JDBC-JNI 實現(xiàn)大幅改進了參數(shù)綁定方式對數(shù)據(jù)寫入(INSERT)場景的支持。采用這種方式寫入數(shù)據(jù)時,能避免 SQL 語法解析的資源消耗,從而在很多情況下顯著提升寫入性能。 注意:

  • JDBC-RESTful 實現(xiàn)并不提供參數(shù)綁定這種使用方式
  • 以下示例代碼基于taos-jdbcdriver-2.0.36
  • binary類型數(shù)據(jù)需要調(diào)用setString方法,nchar類型數(shù)據(jù)需要調(diào)用setNString方法
  • setString 和 setNString 都要求用戶在 size 參數(shù)里聲明表定義中對應(yīng)列的列寬

示例代碼:

public class ParameterBindingDemo {

    private static final String host = "127.0.0.1";
    private static final Random random = new Random(System.currentTimeMillis());
    private static final int BINARY_COLUMN_SIZE = 20;
    private static final String[] schemaList = {
            "create table stable1(ts timestamp, f1 tinyint, f2 smallint, f3 int, f4 bigint) tags(t1 tinyint, t2 smallint, t3 int, t4 bigint)",
            "create table stable2(ts timestamp, f1 float, f2 double) tags(t1 float, t2 double)",
            "create table stable3(ts timestamp, f1 bool) tags(t1 bool)",
            "create table stable4(ts timestamp, f1 binary(" + BINARY_COLUMN_SIZE + ")) tags(t1 binary(" + BINARY_COLUMN_SIZE + "))",
            "create table stable5(ts timestamp, f1 nchar(" + BINARY_COLUMN_SIZE + ")) tags(t1 nchar(" + BINARY_COLUMN_SIZE + "))"
    };
    private static final int numOfSubTable = 10, numOfRow = 10;

    public static void main(String[] args) throws SQLException {

        String jdbcUrl = "jdbc:TAOS://" + host + ":6030/";
        Connection conn = DriverManager.getConnection(jdbcUrl, "root", "taosdata");

        init(conn);

        bindInteger(conn);

        bindFloat(conn);

        bindBoolean(conn);

        bindBytes(conn);

        bindString(conn);

        conn.close();
    }

    private static void init(Connection conn) throws SQLException {
        try (Statement stmt = conn.createStatement()) {
            stmt.execute("drop database if exists test_parabind");
            stmt.execute("create database if not exists test_parabind");
            stmt.execute("use test_parabind");
            for (int i = 0; i < schemaList.length; i++) {
                stmt.execute(schemaList[i]);
            }
        }
    }

    private static void bindInteger(Connection conn) throws SQLException {
        String sql = "insert into ? using stable1 tags(?,?,?,?) values(?,?,?,?,?)";

        try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) {

            for (int i = 1; i <= numOfSubTable; i++) {
                // set table name
                pstmt.setTableName("t1_" + i);
                // set tags
                pstmt.setTagByte(0, Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE))));
                pstmt.setTagShort(1, Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE))));
                pstmt.setTagInt(2, random.nextInt(Integer.MAX_VALUE));
                pstmt.setTagLong(3, random.nextLong());
                // set columns
                ArrayList<Long> tsList = new ArrayList<>();
                long current = System.currentTimeMillis();
                for (int j = 0; j < numOfRow; j++)
                    tsList.add(current + j);
                pstmt.setTimestamp(0, tsList);

                ArrayList<Byte> f1List = new ArrayList<>();
                for (int j = 0; j < numOfRow; j++)
                    f1List.add(Byte.parseByte(Integer.toString(random.nextInt(Byte.MAX_VALUE))));
                pstmt.setByte(1, f1List);

                ArrayList<Short> f2List = new ArrayList<>();
                for (int j = 0; j < numOfRow; j++)
                    f2List.add(Short.parseShort(Integer.toString(random.nextInt(Short.MAX_VALUE))));
                pstmt.setShort(2, f2List);

                ArrayList<Integer> f3List = new ArrayList<>();
                for (int j = 0; j < numOfRow; j++)
                    f3List.add(random.nextInt(Integer.MAX_VALUE));
                pstmt.setInt(3, f3List);

                ArrayList<Long> f4List = new ArrayList<>();
                for (int j = 0; j < numOfRow; j++)
                    f4List.add(random.nextLong());
                pstmt.setLong(4, f4List);

                // add column
                pstmt.columnDataAddBatch();
            }
            // execute column
            pstmt.columnDataExecuteBatch();
        }
    }

    private static void bindFloat(Connection conn) throws SQLException {
        String sql = "insert into ? using stable2 tags(?,?) values(?,?,?)";

        TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class);

        for (int i = 1; i <= numOfSubTable; i++) {
            // set table name
            pstmt.setTableName("t2_" + i);
            // set tags
            pstmt.setTagFloat(0, random.nextFloat());
            pstmt.setTagDouble(1, random.nextDouble());
            // set columns
            ArrayList<Long> tsList = new ArrayList<>();
            long current = System.currentTimeMillis();
            for (int j = 0; j < numOfRow; j++)
                tsList.add(current + j);
            pstmt.setTimestamp(0, tsList);

            ArrayList<Float> f1List = new ArrayList<>();
            for (int j = 0; j < numOfRow; j++)
                f1List.add(random.nextFloat());
            pstmt.setFloat(1, f1List);

            ArrayList<Double> f2List = new ArrayList<>();
            for (int j = 0; j < numOfRow; j++)
                f2List.add(random.nextDouble());
            pstmt.setDouble(2, f2List);

            // add column
            pstmt.columnDataAddBatch();
        }
        // execute
        pstmt.columnDataExecuteBatch();
        // close if no try-with-catch statement is used
        pstmt.close();
    }

    private static void bindBoolean(Connection conn) throws SQLException {
        String sql = "insert into ? using stable3 tags(?) values(?,?)";

        try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) {
            for (int i = 1; i <= numOfSubTable; i++) {
                // set table name
                pstmt.setTableName("t3_" + i);
                // set tags
                pstmt.setTagBoolean(0, random.nextBoolean());
                // set columns
                ArrayList<Long> tsList = new ArrayList<>();
                long current = System.currentTimeMillis();
                for (int j = 0; j < numOfRow; j++)
                    tsList.add(current + j);
                pstmt.setTimestamp(0, tsList);

                ArrayList<Boolean> f1List = new ArrayList<>();
                for (int j = 0; j < numOfRow; j++)
                    f1List.add(random.nextBoolean());
                pstmt.setBoolean(1, f1List);

                // add column
                pstmt.columnDataAddBatch();
            }
            // execute
            pstmt.columnDataExecuteBatch();
        }
    }

    private static void bindBytes(Connection conn) throws SQLException {
        String sql = "insert into ? using stable4 tags(?) values(?,?)";

        try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) {

            for (int i = 1; i <= numOfSubTable; i++) {
                // set table name
                pstmt.setTableName("t4_" + i);
                // set tags
                pstmt.setTagString(0, new String("abc"));

                // set columns
                ArrayList<Long> tsList = new ArrayList<>();
                long current = System.currentTimeMillis();
                for (int j = 0; j < numOfRow; j++)
                    tsList.add(current + j);
                pstmt.setTimestamp(0, tsList);

                ArrayList<String> f1List = new ArrayList<>();
                for (int j = 0; j < numOfRow; j++) {
                    f1List.add(new String("abc"));
                }
                pstmt.setString(1, f1List, BINARY_COLUMN_SIZE);

                // add column
                pstmt.columnDataAddBatch();
            }
            // execute
            pstmt.columnDataExecuteBatch();
        }
    }

    private static void bindString(Connection conn) throws SQLException {
        String sql = "insert into ? using stable5 tags(?) values(?,?)";

        try (TSDBPreparedStatement pstmt = conn.prepareStatement(sql).unwrap(TSDBPreparedStatement.class)) {

            for (int i = 1; i <= numOfSubTable; i++) {
                // set table name
                pstmt.setTableName("t5_" + i);
                // set tags
                pstmt.setTagNString(0, "北京-abc");

                // set columns
                ArrayList<Long> tsList = new ArrayList<>();
                long current = System.currentTimeMillis();
                for (int j = 0; j < numOfRow; j++)
                    tsList.add(current + j);
                pstmt.setTimestamp(0, tsList);

                ArrayList<String> f1List = new ArrayList<>();
                for (int j = 0; j < numOfRow; j++) {
                    f1List.add("北京-abc");
                }
                pstmt.setNString(1, f1List, BINARY_COLUMN_SIZE);

                // add column
                pstmt.columnDataAddBatch();
            }
            // execute
            pstmt.columnDataExecuteBatch();
        }
    }
}

用于設(shè)定 TAGS 取值的方法總共有:

public void setTagNull(int index, int type)
public void setTagBoolean(int index, boolean value)
public void setTagInt(int index, int value)
public void setTagByte(int index, byte value)
public void setTagShort(int index, short value)
public void setTagLong(int index, long value)
public void setTagTimestamp(int index, long value)
public void setTagFloat(int index, float value)
public void setTagDouble(int index, double value)
public void setTagString(int index, String value)
public void setTagNString(int index, String value)

用于設(shè)定 VALUES 數(shù)據(jù)列的取值的方法總共有:

public void setInt(int columnIndex, ArrayList<Integer> list) throws SQLException
public void setFloat(int columnIndex, ArrayList<Float> list) throws SQLException
public void setTimestamp(int columnIndex, ArrayList<Long> list) throws SQLException
public void setLong(int columnIndex, ArrayList<Long> list) throws SQLException
public void setDouble(int columnIndex, ArrayList<Double> list) throws SQLException
public void setBoolean(int columnIndex, ArrayList<Boolean> list) throws SQLException
public void setByte(int columnIndex, ArrayList<Byte> list) throws SQLException
public void setShort(int columnIndex, ArrayList<Short> list) throws SQLException
public void setString(int columnIndex, ArrayList<String> list, int size) throws SQLException
public void setNString(int columnIndex, ArrayList<String> list, int size) throws SQLException

無模式寫入

從 2.2.0.0 版本開始,TDengine 增加了對無模式寫入功能。無模式寫入兼容 InfluxDB 的 行協(xié)議(Line Protocol)、OpenTSDB 的 telnet 行協(xié)議和 OpenTSDB 的 JSON 格式協(xié)議。詳情請參見無模式寫入。

注意:

  • JDBC-RESTful 實現(xiàn)并不提供無模式寫入這種使用方式
  • 以下示例代碼基于taos-jdbcdriver-2.0.36

示例代碼:

public class SchemalessInsertTest {
    private static final String host = "127.0.0.1";
    private static final String lineDemo = "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000";
    private static final String telnetDemo = "stb0_0 1626006833 4 host=host0 interface=eth0";
    private static final String jsonDemo = "{\"metric\": \"meter_current\",\"timestamp\": 1346846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"Beijing\", \"id\": \"d1001\"}}";

    public static void main(String[] args) throws SQLException {
        final String url = "jdbc:TAOS://" + host + ":6030/?user=root&password=taosdata";
        try (Connection connection = DriverManager.getConnection(url)) {
            init(connection);

            SchemalessWriter writer = new SchemalessWriter(connection);
            writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO_SECONDS);
            writer.write(telnetDemo, SchemalessProtocolType.TELNET, SchemalessTimestampType.MILLI_SECONDS);
            writer.write(jsonDemo, SchemalessProtocolType.JSON, SchemalessTimestampType.NOT_CONFIGURED);
        }
    }

    private static void init(Connection connection) throws SQLException {
        try (Statement stmt = connection.createStatement()) {
            stmt.executeUpdate("drop database if exists test_schemaless");
            stmt.executeUpdate("create database if not exists test_schemaless");
            stmt.executeUpdate("use test_schemaless");
        }
    }
}

設(shè)置客戶端參數(shù)

從TDengine-2.3.5.0版本開始,jdbc driver支持在應(yīng)用的第一次連接中,設(shè)置TDengine的客戶端參數(shù)。Driver支持JDBC-JNI方式中,通過jdbcUrl和properties兩種方式設(shè)置client parameter。

注意:

  • JDBC-RESTful不支持設(shè)置client parameter的功能。
  • 應(yīng)用中設(shè)置的client parameter為進程級別的,即如果要更新client的參數(shù),需要重啟應(yīng)用。這是因為client parameter是全局參數(shù),僅在應(yīng)用程序的第一次設(shè)置生效。
  • 以下示例代碼基于taos-jdbcdriver-2.0.36。

示例代碼:

public class ClientParameterSetting {
    private static final String host = "127.0.0.1";

    public static void main(String[] args) throws SQLException {
        setParameterInJdbcUrl();

        setParameterInProperties();
    }

    private static void setParameterInJdbcUrl() throws SQLException {
        String jdbcUrl = "jdbc:TAOS://" + host + ":6030/?debugFlag=135&asyncLog=0";

        Connection connection = DriverManager.getConnection(jdbcUrl, "root", "taosdata");

        printDatabase(connection);

        connection.close();
    }

    private static void setParameterInProperties() throws SQLException {
        String jdbcUrl = "jdbc:TAOS://" + host + ":6030/";
        Properties properties = new Properties();
        properties.setProperty("user", "root");
        properties.setProperty("password", "taosdata");
        properties.setProperty("debugFlag", "135");
        properties.setProperty("asyncLog", "0");
        properties.setProperty("maxSQLLength", "1048576");

        try (Connection conn = DriverManager.getConnection(jdbcUrl, properties)) {
            printDatabase(conn);
        }
    }

    private static void printDatabase(Connection connection) throws SQLException {
        try (Statement stmt = connection.createStatement()) {
            ResultSet rs = stmt.executeQuery("show databases");

            ResultSetMetaData meta = rs.getMetaData();
            while (rs.next()) {
                for (int i = 1; i <= meta.getColumnCount(); i++) {
                    System.out.print(meta.getColumnLabel(i) + ": " + rs.getString(i) + "\t");
                }
                System.out.println();
            }
        }
    }
}

訂閱

創(chuàng)建

TSDBSubscribe sub = ((TSDBConnection)conn).subscribe("topic", "select * from meters", false);

subscribe 方法的三個參數(shù)含義如下:

  • topic:訂閱的主題(即名稱),此參數(shù)是訂閱的唯一標識
  • sql:訂閱的查詢語句,此語句只能是 select 語句,只應(yīng)查詢原始數(shù)據(jù),只能按時間正序查詢數(shù)據(jù)
  • restart:如果訂閱已經(jīng)存在,是重新開始,還是繼續(xù)之前的訂閱

如上面的例子將使用 SQL 語句 select * from meters 創(chuàng)建一個名為 topic 的訂閱,如果這個訂閱已經(jīng)存在,將繼續(xù)之前的查詢進度,而不是從頭開始消費所有的數(shù)據(jù)。

消費數(shù)據(jù)

int total = 0;
while(true) {
    TSDBResultSet rs = sub.consume();
    int count = 0;
    while(rs.next()) {
        count++;
    }
    total += count;
    System.out.printf("%d rows consumed, total %d\n", count, total);
    Thread.sleep(1000);
}

consume 方法返回一個結(jié)果集,其中包含從上次 consume 到目前為止的所有新數(shù)據(jù)。請務(wù)必按需選擇合理的調(diào)用 consume 的頻率(如例子中的 Thread.sleep(1000)),否則會給服務(wù)端造成不必要的壓力。

關(guān)閉訂閱

sub.close(true);

close 方法關(guān)閉一個訂閱。如果其參數(shù)為 true 表示保留訂閱進度信息,后續(xù)可以創(chuàng)建同名訂閱繼續(xù)消費數(shù)據(jù);如為 false 則不保留訂閱進度。

關(guān)閉資源

resultSet.close();
stmt.close();
conn.close();

注意務(wù)必要將 connection 進行關(guān)閉,否則會出現(xiàn)連接泄露。

與連接池使用

HikariCP

使用示例如下:

 public static void main(String[] args) throws SQLException {
    HikariConfig config = new HikariConfig();
    // jdbc properties
    config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/log");
    config.setUsername("root");
    config.setPassword("taosdata");
    // connection pool configurations
    config.setMinimumIdle(10);           //minimum number of idle connection
    config.setMaximumPoolSize(10);      //maximum number of connection in the pool
    config.setConnectionTimeout(30000); //maximum wait milliseconds for get connection from pool
    config.setMaxLifetime(0);       // maximum life time for each connection
    config.setIdleTimeout(0);       // max idle time for recycle idle connection
    config.setConnectionTestQuery("select server_status()"); //validation query

    HikariDataSource ds = new HikariDataSource(config); //create datasource

    Connection  connection = ds.getConnection(); // get connection
    Statement statement = connection.createStatement(); // get statement

    //query or insert
    // ...

    connection.close(); // put back to conneciton pool
}

通過 HikariDataSource.getConnection() 獲取連接后,使用完成后需要調(diào)用 close() 方法,實際上它并不會關(guān)閉連接,只是放回連接池中。 更多 HikariCP 使用問題請查看官方說明。

Druid

使用示例如下:

public static void main(String[] args) throws Exception {

    DruidDataSource dataSource = new DruidDataSource();
    // jdbc properties
    dataSource.setDriverClassName("com.taosdata.jdbc.TSDBDriver");
    dataSource.setUrl(url);
    dataSource.setUsername("root");
    dataSource.setPassword("taosdata");
    // pool configurations
    dataSource.setInitialSize(10);
    dataSource.setMinIdle(10);
    dataSource.setMaxActive(10);
    dataSource.setMaxWait(30000);
    dataSource.setValidationQuery("select server_status()");

    Connection  connection = dataSource.getConnection(); // get connection
    Statement statement = connection.createStatement(); // get statement
    //query or insert 
    // ...

    connection.close(); // put back to conneciton pool
}

更多 druid 使用問題請查看官方說明

注意事項:

  • TDengine v1.6.4.1 版本開始提供了一個專門用于心跳檢測的函數(shù) select server_status(),所以在使用連接池時推薦使用 select server_status() 進行 Validation Query。

如下所示,select server_status() 執(zhí)行成功會返回 1。

taos> select server_status();
server_status()|
================
1              |
Query OK, 1 row(s) in set (0.000141s)

在框架中使用

示例程序

示例程序源碼位于TDengine/test/examples/JDBC下:

  • JDBCDemo:JDBC示例源程序
  • JDBCConnectorChecker:JDBC安裝校驗源程序及jar包
  • Springbootdemo:springboot示例源程序
  • SpringJdbcTemplate:SpringJDBC模板

請參考:JDBC example

常見問題

  • 使用 Statement 的 addBatch() 和 executeBatch() 來執(zhí)行“批量寫入/更新”,為什么沒有帶來性能上的提升? 原因:TDengine 的 JDBC 實現(xiàn)中,通過 addBatch() 方法提交的sql語句,會按照添加的順序,依次執(zhí)行,這種方式?jīng)]有減少與服務(wù)端的交互次數(shù),不會帶來性能上的提升。 解決方法:1. 在一條 insert 語句中拼接多個 values 值;2. 使用多線程的方式并發(fā)插入;3. 使用參數(shù)綁定的寫入方式

  • java.lang.UnsatisfiedLinkError: no taos in java.library.path 原因:程序沒有找到依賴的本地函數(shù)庫 taos。 解決方法:Windows 下可以將 C:\TDengine\driver\taos.dll 拷貝到 C:\Windows\System32\ 目錄下,Linux 下將建立如下軟鏈 ln -s /usr/local/taos/driver/libtaos.so.x.x.x.x /usr/lib/libtaos.so 即可。

  • java.lang.UnsatisfiedLinkError: taos.dll Can't load AMD 64 bit on a IA 32-bit platform 原因:目前 TDengine 只支持 64 位 JDK。 解決方法:重新安裝 64 位 JDK。

  • 其它問題請參考 Issues