Java Connector

    taos-jdbcdriver 的实现包括 2 种形式: JDBC-JNI 和 JDBC-RESTful(taos-jdbcdriver-2.0.18 开始支持 JDBC-RESTful)。 JDBC-JNI 通过调用客户端 libtaos.so(或 taos.dll )的本地方法实现, JDBC-RESTful 则在内部封装了 RESTful 接口实现。

    上图显示了 3 种 Java 应用使用连接器访问 TDengine 的方式:

    • JDBC-JNI:Java 应用在物理节点1(pnode1)上使用 JDBC-JNI 的 API ,直接调用客户端 API(libtaos.so 或 taos.dll)将写入和查询请求发送到位于物理节点2(pnode2)上的 taosd 实例。
    • RESTful:应用将 SQL 发送给位于物理节点2(pnode2)上的 RESTful 连接器,再调用客户端 API(libtaos.so)。
    • JDBC-RESTful:Java 应用通过 JDBC-RESTful 的 API ,将 SQL 封装成一个 RESTful 请求,发送给物理节点2的 RESTful 连接器。

    TDengine 的 JDBC 驱动实现尽可能与关系型数据库驱动保持一致,但时序空间数据库与关系对象型数据库服务的对象和技术特征存在差异,导致 taos-jdbcdriver 与传统的 JDBC driver 也存在一定差异。在使用时需要注意以下几点:

    • TDengine 目前不支持针对单条数据记录的删除操作。
    • 目前不支持事务操作。
    • 目前不支持表间的 union 操作。
    • 目前不支持嵌套查询(nested query)。
    • 对每个 Connection 的实例,至多只能有一个打开的 ResultSet 实例;如果在 ResultSet 还没关闭的情况下执行了新的查询,taos-jdbcdriver 会自动关闭上一个 ResultSet。

    如何获取 taos-jdbcdriver

    目前 taos-jdbcdriver 已经发布到 仓库,且各大仓库都已同步。

    maven 项目中使用如下 pom.xml 配置即可:

    源码编译打包

    下载 源码之后,进入 taos-jdbcdriver 源码目录 src/connector/jdbc 执行 mvn clean package -Dmaven.test.skip=true 即可生成相应 jar 包。

    获取连接

    指定URL获取连接

    通过指定URL获取连接,如下所示:

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

    以上示例,使用 JDBC-RESTful 的 driver,建立了到 hostname 为 taosdemo.com,端口为 6041,数据库名为 test 的连接。这个 URL 中指定用户名(user)为 root,密码(password)为 taosdata。

    使用 JDBC-RESTful 接口,不需要依赖本地函数库。与 JDBC-JNI 相比,仅需要:

    1. driverClass 指定为“com.taosdata.jdbc.rs.RestfulDriver”;
    2. jdbcUrl 以“jdbc:TAOS-RS://”开头;
    3. 使用 6041 作为连接端口。

    如果希望获得更好的写入和查询性能,Java 应用可以使用 JDBC-JNI 的driver,如下所示:

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

    以上示例,使用了 JDBC-JNI 的 driver,建立了到 hostname 为 taosdemo.com,端口为 6030(TDengine 的默认端口),数据库名为 test 的连接。这个 URL 中指定用户名(user)为 root,密码(password)为 taosdata。

    注意:使用 JDBC-JNI 的 driver,taos-jdbcdriver 驱动包时需要依赖系统对应的本地函数库。

    • libtaos.so 在 linux 系统中成功安装 TDengine 后,依赖的本地函数库 libtaos.so 文件会被自动拷贝至 /usr/lib/libtaos.so,该目录包含在 Linux 自动扫描路径上,无需单独指定。

    • taos.dll 在 windows 系统中安装完客户端之后,驱动包依赖的 taos.dll 文件会自动拷贝到系统默认搜索路径 C:/Windows/System32 下,同样无需要单独指定。

    TDengine 的 JDBC URL 规范格式为: jdbc:[TAOS|TAOS-RS]://[host_name]:[port]/[database_name]?[user={user}|&password={password}|&charset={charset}|&cfgdir={config_dir}|&locale={locale}|&timezone={timezone}]

    url中的配置参数如下:

    • user:登录 TDengine 用户名,默认值 root。
    • password:用户登录密码,默认值 taosdata。
    • cfgdir:客户端配置文件目录路径,Linux OS 上默认值 /etc/taos ,Windows OS 上默认值 C:/TDengine/cfg。
    • charset:客户端使用的字符集,默认值为系统字符集。
    • locale:客户端语言环境,默认值系统当前 locale。
    • timezone:客户端使用的时区,默认值为系统当前时区。

    指定URL和Properties获取连接

    除了通过指定的 URL 获取连接,还可以使用 Properties 指定建立连接时的参数,如下所示:

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

    以上示例,建立一个到 hostname 为 taosdemo.com,端口为 6030,数据库名为 test 的连接。注释为使用 JDBC-RESTful 时的方法。这个连接在 url 中指定了用户名(user)为 root,密码(password)为 taosdata,并在 connProps 中指定了使用的字符集、语言环境、时区等信息。

    properties 中的配置参数如下:

    • 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:客户端使用的字符集,默认值为系统字符集。
    • TSDBDriver.PROPERTY_KEY_LOCALE:客户端语言环境,默认值系统当前 locale。

    使用客户端配置文件建立连接

    当使用 JDBC-JNI 连接 TDengine 集群时,可以使用客户端配置文件,在客户端配置文件中指定集群的 firstEp、secondEp参数。 如下所示:

    1. 在 Java 应用中不指定 hostname 和 port
    1. public Connection getConn() throws Exception{
    2. Class.forName("com.taosdata.jdbc.TSDBDriver");
    3. String jdbcUrl = "jdbc:TAOS://:/test?user=root&password=taosdata";
    4. Properties connProps = new Properties();
    5. connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
    6. connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");
    7. connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
    8. Connection conn = DriverManager.getConnection(jdbcUrl, connProps);
    9. return conn;
    10. }
    1. 在配置文件中指定 firstEp 和 secondEp
    1. # first fully qualified domain name (FQDN) for TDengine system
    2. firstEp cluster_node1:6030
    3. # second fully qualified domain name (FQDN) for TDengine system, for cluster only
    4. secondEp cluster_node2:6030
    5. # default system charset
    6. # charset UTF-8
    7. # system locale
    8. # locale en_US.UTF-8

    以上示例,jdbc 会使用客户端的配置文件,建立到 hostname 为 cluster_node1、端口为 6030、数据库名为 test 的连接。当集群中 firstEp 节点失效时,JDBC 会尝试使用 secondEp 连接集群。 TDengine 中,只要保证 firstEp 和 secondEp 中一个节点有效,就可以正常建立到集群的连接。

    注意:这里的配置文件指的是调用 JDBC Connector 的应用程序所在机器上的配置文件,Linux OS 上默认值 /etc/taos/taos.cfg ,Windows OS 上默认值 C://TDengine/cfg/taos.cfg。

    配置参数的优先级

    通过以上 3 种方式获取连接,如果配置参数在 url、Properties、客户端配置文件中有重复,则参数的优先级由高到低分别如下:

    1. JDBC URL 参数,如上所述,可以在 JDBC URL 的参数中指定。
    2. Properties connProps
    3. 客户端配置文件 taos.cfg

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

    更多详细配置请参考客户端配置

    插入数据

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

    now 为系统内部函数,默认为服务器当前时间。 now + 1s 代表服务器当前时间往后加 1 秒,数字后面代表时间单位:a(毫秒), s(秒), m(分), h(小时), d(天),w(周), n(月), y(年)。

    查询数据

    1. // query data
    2. ResultSet resultSet = stmt.executeQuery("select * from tb");
    3. int temperature = 0;
    4. float humidity = 0;
    5. while(resultSet.next()){
    6. ts = resultSet.getTimestamp(1);
    7. temperature = resultSet.getInt(2);
    8. humidity = resultSet.getFloat("humidity");
    9. System.out.printf("%s, %d, %s\n", ts, temperature, humidity);
    10. }

    查询和操作关系型数据库一致,使用下标获取返回字段内容时从 1 开始,建议使用字段名称获取。

    在报错后,通过SQLException可以获取到错误的信息和错误码:

    1. try (Statement statement = connection.createStatement()) {
    2. // executeQuery
    3. ResultSet resultSet = statement.executeQuery(sql);
    4. // print result
    5. printResult(resultSet);
    6. } catch (SQLException e) {
    7. System.out.println("ERROR Message: " + e.getMessage());
    8. System.out.println("ERROR Code: " + e.getErrorCode());
    9. e.printStackTrace();
    10. }

    JDBC连接器可能报错的错误码包括3种:JDBC driver本身的报错(错误码在0x2301到0x2350之间),JNI方法的报错(错误码在0x2351到0x2400之间),TDengine其他功能模块的报错。 具体的错误码请参考:

    订阅

    创建

    1. TSDBSubscribe sub = ((TSDBConnection)conn).subscribe("topic", "select * from meters", false);
    • topic:订阅的主题(即名称),此参数是订阅的唯一标识
    • sql:订阅的查询语句,此语句只能是 select 语句,只应查询原始数据,只能按时间正序查询数据
    • restart:如果订阅已经存在,是重新开始,还是继续之前的订阅

    如上面的例子将使用 SQL 语句 select * from meters 创建一个名为 topic 的订阅,如果这个订阅已经存在,将继续之前的查询进度,而不是从头开始消费所有的数据。

    消费数据

    1. int total = 0;
    2. while(true) {
    3. TSDBResultSet rs = sub.consume();
    4. int count = 0;
    5. while(rs.next()) {
    6. count++;
    7. }
    8. total += count;
    9. System.out.printf("%d rows consumed, total %d\n", count, total);
    10. }

    consume 方法返回一个结果集,其中包含从上次 consume 到目前为止的所有新数据。请务必按需选择合理的调用 consume 的频率(如例子中的 Thread.sleep(1000)),否则会给服务端造成不必要的压力。

    关闭订阅

    close 方法关闭一个订阅。如果其参数为 true 表示保留订阅进度信息,后续可以创建同名订阅继续消费数据;如为 false 则不保留订阅进度。

    关闭资源

    1. resultSet.close();
    2. stmt.close();
    3. conn.close();

    与连接池使用

    HikariCP

    • 引入相应 HikariCP maven 依赖:
    1. <dependency>
    2. <groupId>com.zaxxer</groupId>
    3. <artifactId>HikariCP</artifactId>
    4. <version>3.4.1</version>
    5. </dependency>
    • 使用示例如下:
    1. public static void main(String[] args) throws SQLException {
    2. HikariConfig config = new HikariConfig();
    3. config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/log");
    4. config.setUsername("root");
    5. config.setPassword("taosdata");
    6. // connection pool configurations
    7. config.setMinimumIdle(10); //minimum number of idle connection
    8. config.setMaximumPoolSize(10); //maximum number of connection in the pool
    9. config.setConnectionTimeout(30000); //maximum wait milliseconds for get connection from pool
    10. config.setMaxLifetime(0); // maximum life time for each connection
    11. config.setIdleTimeout(0); // max idle time for recycle idle connection
    12. config.setConnectionTestQuery("select server_status()"); //validation query
    13. HikariDataSource ds = new HikariDataSource(config); //create datasource
    14. Connection connection = ds.getConnection(); // get connection
    15. Statement statement = connection.createStatement(); // get statement
    16. //query or insert
    17. // ...
    18. connection.close(); // put back to conneciton pool
    19. }

    通过 HikariDataSource.getConnection() 获取连接后,使用完成后需要调用 close() 方法,实际上它并不会关闭连接,只是放回连接池中。 更多 HikariCP 使用问题请查看

    Druid

    • 引入相应 Druid maven 依赖:
    1. <dependency>
    2. <groupId>com.alibaba</groupId>
    3. <artifactId>druid</artifactId>
    4. <version>1.1.20</version>
    5. </dependency>
    • 使用示例如下:
    1. public static void main(String[] args) throws Exception {
    2. DruidDataSource dataSource = new DruidDataSource();
    3. // jdbc properties
    4. dataSource.setDriverClassName("com.taosdata.jdbc.TSDBDriver");
    5. dataSource.setUrl(url);
    6. dataSource.setUsername("root");
    7. dataSource.setPassword("taosdata");
    8. // pool configurations
    9. dataSource.setInitialSize(10);
    10. dataSource.setMinIdle(10);
    11. dataSource.setMaxActive(10);
    12. dataSource.setMaxWait(30000);
    13. dataSource.setValidationQuery("select server_status()");
    14. Connection connection = dataSource.getConnection(); // get connection
    15. Statement statement = connection.createStatement(); // get statement
    16. //query or insert
    17. // ...
    18. connection.close(); // put back to conneciton pool
    19. }

    更多 druid 使用问题请查看官方说明

    注意事项

    • TDengine v1.6.4.1 版本开始提供了一个专门用于心跳检测的函数 select server_status(),所以在使用连接池时推荐使用 select server_status() 进行 Validation Query。

    如下所示,select server_status() 执行成功会返回 1

    • Spring JdbcTemplate 中使用 taos-jdbcdriver,可参考
    • Springboot + Mybatis 中使用,可参考 springbootdemo

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

    TDengine 目前支持时间戳、数字、字符、布尔类型,与 Java 对应类型转换如下:

    常见问题

    • java.lang.UnsatisfiedLinkError: no taos in java.library.path

      原因:程序没有找到依赖的本地函数库 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。