JAVA 驱动

推荐使用 OceanBase 的 JDBC 驱动:oceanbase-client。

连接 MySQL 租户也可以使用 MySQL 官方 JDBC 驱动:mysql-connector-Java。推荐使用 5.1.30 和 5.1.40 版本,其他版本可能存在兼容问题。

注意

  • oceanbase-client 完全兼容 MySQL JDBC 的使用方式并且可以自动识别 OceanBase 的运行模式是 MySQL 还是 Oracle,因为其在协议层还兼容 2 种模式,且兼容 OB 2.0 协议。
  • mysql-connector-Java 只支持 MySQL 运行模式。

oceanbase-client 使用说明

连接串的前缀需要设置为 jdbc:oceanbase ,其他部分的使用方式与原生的 MySQL 使用方式保持一致。

注意

1.0.9 版本的 oceanbase-client 的驱动类名为:com.alipay.oceanbase.obproxy.mysql.jdbc.Driver。后续版本驱动类名改为:com.alipay.oceanbase.jdbc.Driver。

代码示例如下所示:

  1. String url = "jdbc:oceanbase://xxx.xxx.xxx.xxx:2883/SYS?useUnicode=true&characterEncoding=utf-8"; //IP地址:OBProxy端口号/数据库名
  2. String username = "SYS@test1#obtest"; //用户名@租户名#集群名称
  3. String password = "test"; //密码
  4. Connection conn = null;
  5. try {
  6. Class.forName("com.alipay.oceanbase.obproxy.mysql.jdbc.Driver"); //驱动类名
  7. conn = DriverManager.getConnection(url, username, password);
  8. PreparedStatement ps = conn.prepareStatement("select to_char(sysdate,'yyyy-MM-dd HH24:mi:ss') from dual;");
  9. ResultSet rs = ps.executeQuery();
  10. rs.next();
  11. System.out.println("sysdate is:" + rs.getString(1));
  12. rs.close();
  13. ps.close();
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. } finally {
  17. if (null != conn) {
  18. conn.close();
  19. }
  20. }

mysql-connector-Java 使用说明

连接串的前缀需要设置为 jdbc:mysql,驱动类名为:com.mysql.jdbc.Driver。

代码示例如下所示:

  1. String url = "jdbc:mysql://xxx.xxx.xxx.xxx:2883/hr?useUnicode=true&characterEncoding=utf-8"; //IP地址:OBProxy端口号/数据库名
  2. String username = "root@test2#obtest"; //用户名@租户名#集群名称
  3. String password = "test"; //密码
  4. Connection conn = null;
  5. try {
  6. Class.forName("com.mysql.jdbc.Driver"); //驱动类名
  7. conn = DriverManager.getConnection(url, username, password);
  8. PreparedStatement ps = conn.prepareStatement("select date_format(now(),'%Y-%m-%d %H:%i:%s');");
  9. ResultSet rs = ps.executeQuery();
  10. rs.next();
  11. System.out.println("sysdate is:" + rs.getString(1));
  12. rs.close();
  13. ps.close();
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. } finally {
  17. if (null != conn) {
  18. conn.close();
  19. }
  20. }

JDBC 驱动实践

下述表格中展示了 JDBC 中几个必须要设置的重要参数。均可以设置到连接池的 ConnectionProperties 中,或者 JdbcURL 上:

参数说明推荐值
readTimeout网络读超时时间,如果不设置默认是 0,使用 OS 默认超时时间。5000ms
connectTimeout链接建立超时时间,如果不设置默认是 0,使用 OS 默认超时时间。500ms

应用连接池配置

将应用和数据库连接进行业务操作,建议使用连接池。如果是 Java 程序,推荐使用 Druid 连接池。配置示例如下所示:

  1. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  2. <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  3. <!-- 基本属性 URL、user、password -->
  4. <property name="url" value="jdbc:mysql://ip:port/db?socketTimeout=30000&amp;connectTimeout=3000" />
  5. <property name="username" value="{user}" />
  6. <property name="password" value="{password}" />
  7. <!-- 配置初始化大小、最小、最大 -->
  8. <property name="maxActive" value="4" /> //initialSize/minIdle/maxActive视业务规模设置
  9. <property name="initialSize" value="2" />
  10. <property name="minIdle" value="2" />
  11. <!-- 获取连接等待超时的时间,单位是毫秒 -->
  12. <property name="maxWait" value="1000" />
  13. <!-- 间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
  14. <property name="timeBetweenEvictionRunsMillis" value="60000" />
  15. <!-- 一个连接在池中最小空闲的时间,单位是毫秒-->
  16. <property name="minEvictableIdleTimeMillis" value="300000" />
  17. <!-- 检测连接是否可用的 SQL -->
  18. <property name="validationQuery" value="SELECT foo FROM bar" /> //找真实的、记录少的业务表用作查询探测语句
  19. <!-- 是否开启空闲连接检查 -->
  20. <property name="testWhileIdle" value="true" />
  21. <!-- 是否在获取连接前检查连接状态 -->
  22. <property name="testOnBorrow" value="false" />
  23. <!-- 是否在归还连接时检查连接状态 -->
  24. <property name="testOnReturn" value="false" />
  25. </bean>

ODBC 驱动

开放数据库互连(ODBC)是微软公司开放服务结构(WOSA,Windows Open Services Architecture)中有关数据库的一个组成部分,基本思想是为用户提供简单、标准、透明的数据库连接的公共编程接口,开发厂商根据 ODBC 的标准去实现底层的驱动程序,这个驱动对用户是透明的,并允许根据不同的 DBMS 采用不同的技术加以优化实现。

ODBC 主要由驱动程序和驱动程序管理器组成。驱动程序是一个用以支持 ODBC 函数调用的模块,每个驱动程序对应于相应的数据库,当应用程序从基于一个数据库系统移植到另一个时,只需更改应用程序中由 ODBC 管理程序设定的与相应数据库系统对应的别名即可。

连接 OceanBase 的驱动程序管理器和驱动程序 OB-ODBC 均是定制开发,通过该驱动可以连接访问 OceanBase 的 Mysql 及 Oracle 租户。下图是 Linux 下 OB-ODBC 连接 OceanBase 的架构:

Image-402.png

连接环境配置

连接数据库前,需要配置连接环境,配置方法如下:

  1. 修改配置文件 odbc.ini,路径为 /etc/odbc.ini ,配置文件 odbc.ini 也可以放在除了 /etc 以外的其他目录,通过设置环境变量到相应的目录即可。
  1. [ODBC Data Sources]
  2. data_source_name = Name
  3. [Name]
  4. Driver=Oceanbase
  5. Description = MyODBC 5 Driver DSN
  6. SERVER = [OBProxy IP地址]
  7. PORT = [OBProxy 端口]
  8. USER = [用户名@租户名#集群名称]
  9. Password = [用户密码]
  10. Database = [数据库名]
  11. OPTION = 3
  12. charset=UTF8
  1. 配置 /etc/odbcinst.ini
  1. [Oceanbase]
  2. Driver=/u01/mysql-odbc/lib/libmyodbc5a.so
  1. 设置环境变量。
  1. export ODBCSYSINI=/etc
  2. export ODBCINI=/etc/odbc.ini
  3. export LD_LIBRARY_PATH=/u01/mysql/lib:/usr/lib64:$LD_LIBRARY_PATH
  4. export PATH=$PATH:/u01/unix-odbc/bin
  1. 配置完成后使用 odbcinst -j 命令查看配置是否正确。

返回结果如下所示:

  1. unixODBC 2.3.7
  2. DRIVERS............: /etc/odbcinst.ini
  3. SYSTEM DATA SOURCES: /etc/odbc.ini
  4. FILE DATA SOURCES..: /etc/ODBCDataSources
  5. USER DATA SOURCES..: /etc/odbc.ini
  6. SQLULEN Size.......: 8
  7. SQLLEN Size........: 8
  8. SQLSETPOSIROW Size.: 8

示例

  1. 以 OceanBase 数据库的 Oracle 租户为测试环境,运行下述语句在数据库中创建一张示例表 TEST。
  1. CREATE TABLE "TEST" (
  2. "ID" NUMBER(38) NOT NULL,
  3. "NAME" VARCHAR2(32),
  4. CONSTRAINT "TEST_PK" PRIMARY KEY ("ID")
  5. ) COMPRESS FOR ARCHIVE REPLICA_NUM = 3 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 10
  1. 编写连接数据库的驱动程序。

驱动程序的代码示例如下:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include "sql.h"
  5. #include "sqlext.h"
  6. typedef struct tagODBCHandler {
  7. SQLHENV henv;
  8. SQLHDBC hdbc;
  9. SQLHSTMT hstmt;
  10. }ODBCHandler;
  11. int IS_SUCC(SQLRETURN retcode) {
  12. if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) return 1;
  13. return 0;
  14. }
  15. void checkError(SQLRETURN retcode, const char* msg, ODBCHandler* handler) {
  16. SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1];
  17. SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1];
  18. SQLINTEGER error;
  19. SQLSMALLINT len;
  20. SQLRETURN tmpcode;
  21. switch (retcode){
  22. case SQL_SUCCESS:
  23. printf("%s retcode is SQLRETURN\n", msg);
  24. break;
  25. case SQL_SUCCESS_WITH_INFO:
  26. printf("%s retcode is SQL_SUCCESS_WITH_INFO\n", msg);
  27. break;
  28. case SQL_ERROR:
  29. printf("%s retcode is SQL_ERROR\n", msg);
  30. tmpcode = SQLError(handler->henv, handler->hdbc, handler->hstmt, sqlstate, &error, message, sizeof(message), &len);
  31. if (tmpcode != SQL_SUCCESS && tmpcode != SQL_SUCCESS_WITH_INFO) {
  32. printf("get sqlerror failed %d", tmpcode);
  33. } else {
  34. printf("error is %d, meeesage is %s, sqlstate is %s, len is %d\n", error, message, sqlstate, len);
  35. }
  36. break;
  37. case SQL_INVALID_HANDLE:
  38. printf("%s retcode is SQL_INVALID_HANDLE\n", msg);
  39. break;
  40. case SQL_STILL_EXECUTING:
  41. printf("%s retcode is SQL_STILL_EXECUTING\n", msg);
  42. break;
  43. case SQL_NO_DATA:
  44. printf("%s retcode is SQL_NO_DATA\n", msg);
  45. break;
  46. default:
  47. printf("%s retcode is UNKNOWN retcode\n", msg);
  48. break;
  49. }
  50. }
  51. int main(int argc, char** argv) {
  52. ODBCHandler handler;
  53. SQLRETURN retcode;
  54. #define MAX_NAME_LEN 255
  55. SQLCHAR connOut[MAX_NAME_LEN+1];
  56. SQLSMALLINT len;
  57. //Allocate environment handle
  58. retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &handler.henv);
  59. // Set the ODBC version environment attribute
  60. if (!IS_SUCC(retcode)) {
  61. checkError(retcode, "SQLAllocHandle", &handler);
  62. return -1;
  63. }
  64. retcode = SQLSetEnvAttr(handler.henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3_80, 0);
  65. // Allocate connection handle
  66. if (!IS_SUCC(retcode)) {
  67. checkError(retcode, "SQLSetEnvAttr", &handler);
  68. return -1;
  69. }
  70. retcode = SQLAllocHandle(SQL_HANDLE_DBC, handler.henv, &handler.hdbc);
  71. if (!IS_SUCC(retcode)) {
  72. checkError(retcode, "SQLAllocHandle", &handler);
  73. return -1;
  74. }
  75. // Set login timeout to 5 seconds
  76. SQLSetConnectAttr(handler.hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
  77. // Connect to data source
  78. retcode = SQLDriverConnect(handler.hdbc, NULL, (SQLCHAR*)"DSN=odbctest", SQL_NTS, connOut, MAX_NAME_LEN, &len,SQL_DRIVER_NOPROMPT);
  79. if (!IS_SUCC(retcode)) {
  80. checkError(retcode, "SQLDriverConnect", &handler);
  81. return -1;
  82. }
  83. retcode = SQLAllocHandle(SQL_HANDLE_STMT, handler.hdbc, &handler.hstmt);
  84. if (!IS_SUCC(retcode)) {
  85. checkError(retcode, "SQLAllocHandle", &handler);
  86. return -1;
  87. }
  88. {
  89. //insert
  90. retcode = SQLPrepare(handler.hstmt, (SQLCHAR*)"INSERT INTO test VALUES(?,'robin')", SQL_NTS);
  91. if (!IS_SUCC(retcode)) {
  92. checkError(retcode, "SQLPrepare", &handler);
  93. return -1;
  94. }
  95. SQLINTEGER id = 2;
  96. retcode = SQLBindParameter(handler.hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &id, 0, NULL);
  97. if (!IS_SUCC(retcode)) {
  98. checkError(retcode, "SQLBindParameter", &handler);
  99. return -1;
  100. }
  101. retcode = SQLExecute(handler.hstmt);
  102. if (!IS_SUCC(retcode)) {
  103. checkError(retcode, "SQLExecute", &handler);
  104. return -1;
  105. }
  106. //ORACLE mode will need this section
  107. retcode = SQLEndTran(SQL_HANDLE_DBC, handler.hdbc, SQL_COMMIT);
  108. if (!IS_SUCC(retcode)) {
  109. checkError(retcode, "SQLCommit", &handler);
  110. return -1;
  111. }
  112. }
  113. // clean handle
  114. SQLFreeHandle(SQL_HANDLE_STMT, handler.hstmt);
  115. SQLDisconnect(handler.hdbc);
  116. SQLFreeHandle(SQL_HANDLE_DBC, handler.hdbc);
  117. SQLFreeHandle(SQL_HANDLE_ENV, handler.henv);
  118. return 0;
  119. }

注意

Oracle 租户同 MySQL 租户最大的区别在于不会自动提交,因此代码中增加了 ORACLE mode will need this section 片段,如果是操作 MySQL 租户则无需这段代码。

  1. 编译并执行驱动程序。

GCC 编译时,使用 -I 选项指定头文件; -L 指定库文件目录; -l 指定库名。

示例语句如下所示:

  1. gcc test.c -L/u01/unix-odbc/lib -lodbc -I/u01/unix-odbc/include -otest
  2. ./test

检查结果:

  1. obclient -hobproxy.oceanbase.abc.com -uhr@test0_5#obtest -p'hr' -P2883
  2. obclient> select * from test;
  3. +----+-------+
  4. | ID | NAME |
  5. +----+-------+
  6. | 1 | jason |
  7. | 2 | robin |
  8. +----+-------+

MySQL C API

C APIs 包含在 mysqlclient 库文件当中,GCC 编译时使用 -I 选项指定头文件, -L 指定库文件目录, -l 指定库名。示例语句如下所示:

  1. gcc test.c -I/usr/include/mysql/ -L/usr/lib64/mysql -lmysqlclient

注意:把库文件名开头的 lib 和结尾的 .so 去掉就是库名。

image

代码示例:

  1. #include <mysql.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. void main(void) {
  5. MYSQL conn;
  6. char server = "xxx.xxx.xxx.xxx";
  7. char user = "root@test#obtest"; //用户名@租户名#集群名称
  8. char password = "test"; //密码
  9. char *database = "test"; //数据库名
  10. char str_sqls;
  11. int status;
  12. int result;
  13. int i;
  14. conn = mysql_init(NULL); / Connect to database /
  15. / connect to server with the CLIENT_MULTI_STATEMENTS option /
  16. if (mysql_real_connect (conn, server, user, password,
  17. database, 3306, NULL, CLIENT_MULTI_STATEMENTS) == NULL)
  18. {
  19. printf("mysql_real_connect() failed\n");
  20. mysql_close(conn);
  21. exit(1);
  22. }
  23. / execute multiple statements /
  24. strcat(str_sqls, "DROP TABLE IF EXISTS test_table;");
  25. strcat(str_sqls, "CREATE TABLE test_table(id BIGINT);");
  26. strcat(str_sqls, "INSERT INTO test_table VALUES(10);");
  27. status = mysql_query(conn, str_sqls);
  28. if (status)
  29. {
  30. printf("Could not execute statement(s)");
  31. mysql_close(conn);
  32. exit(0);
  33. }
  34. mysql_close(conn);
  35. }