diff --git a/media/dev-language/dev-golang1.png b/media/dev-language/dev-golang1.png new file mode 100644 index 0000000000000000000000000000000000000000..52abda985f938bfc31e3c7508e98326369357bba Binary files /dev/null and b/media/dev-language/dev-golang1.png differ diff --git a/media/dev-language/dev-java0.png b/media/dev-language/dev-java0.png new file mode 100644 index 0000000000000000000000000000000000000000..e894bb127cc1508bfa416c5b81bcb89f84082d6d Binary files /dev/null and b/media/dev-language/dev-java0.png differ diff --git a/media/dev-language/dev-java1.png b/media/dev-language/dev-java1.png new file mode 100644 index 0000000000000000000000000000000000000000..78ef4ef51c819748af282b0c5656d3387e762e2c Binary files /dev/null and b/media/dev-language/dev-java1.png differ diff --git a/media/dev-language/dev-language-question.png b/media/dev-language/dev-language-question.png new file mode 100644 index 0000000000000000000000000000000000000000..213564466b88b33e6bbdf74f3929ef0c1c6e7912 Binary files /dev/null and b/media/dev-language/dev-language-question.png differ diff --git a/media/dev-language/dev-python1.png b/media/dev-language/dev-python1.png new file mode 100644 index 0000000000000000000000000000000000000000..fda09516478f7c602897f9fa7b5f21b122f4f6c7 Binary files /dev/null and b/media/dev-language/dev-python1.png differ diff --git "a/\345\274\200\345\217\221\350\277\220\347\273\264-\345\220\204\347\247\215\350\257\255\350\250\200\345\246\202\344\275\225\350\277\236\346\216\245\345\210\260OceanBase.md" "b/\345\274\200\345\217\221\350\277\220\347\273\264-\345\220\204\347\247\215\350\257\255\350\250\200\345\246\202\344\275\225\350\277\236\346\216\245\345\210\260OceanBase.md" new file mode 100644 index 0000000000000000000000000000000000000000..d19cf923001b75a92a890f636d7feca622cca63b --- /dev/null +++ "b/\345\274\200\345\217\221\350\277\220\347\273\264-\345\220\204\347\247\215\350\257\255\350\250\200\345\246\202\344\275\225\350\277\236\346\216\245\345\210\260OceanBase.md" @@ -0,0 +1,685 @@ +本篇文章想跟大家分享一下,各种主流开发语言如何连接到 OceanBase 社区版数据库,进行日常开发。这个问题也是社区群里面问的比较多的一个问题之一,希望通过本篇文章,能给各位参与开发的小伙伴提供一些指导。 + +![](./media/dev-language/dev-language-question.png) + +# 0. 前置条件 + +首先我们在 OceanBase test 数据库中创建一个 t1 表,并向里面插入 2 条数据,用作后面所有语言的测试基础表: + +![](./media/dev-language/dev-java0.png) + +# 1. Java 数据库连接驱动(JDBC) + +首先上来肯定要说一下 Java,说 Java 是第一大应用开发语言应该不为过吧?OceanBase 社区版对 MySQL 5.7 的兼容性最好,所以您可以使用 MySQL 5.7 的 JDBC 包(建议使用 mysql-connector-java-5.1.47 版本,MySQL 5.7 对应的 JDBC 版本为 5.1.x),也可以使用 OceanBase 提供的自有 JDBC 包,下面我们分别用代码演示一个简单的示例。 + +> Java 数据库连接(JDBC)是一种 API,它使 Java 能够将 SQL 语句发送到对象关系数据库,如 Oracle、MySQL 数据库。JDBC 支持为 Java 暴露 SQL 数据类型,并快速访问 SQL 数据。 + +## 1.1 MySQL JDBC 连接 Demo + +我们首先从 https://downloads.mysql.com/archives/c-j/ 下载对应的 5.1.47 的 jar 包,创建一个新的 Demo 项目,把刚才的 jar 包加载到类库中,然后用最朴素的 Java 代码执行增删改查操作,如下: + +```java +import java.sql.*; +public class JavaDemo { + public static void main(String[] args) throws ClassNotFoundException,SQLException { + //1.加载驱动 + Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动 + //2.用户信息和url + String url="jdbc:mysql://10.211.55.73:2883/test?useUnicode=true&characterEncoding=utf8&&useSSL=true"; + String username = "root"; + String password = "observer"; + //3.连接成功,连接数据库对象 Connection + Connection connection = DriverManager.getConnection(url,username,password); + //4.执行SQL对象 Statement + Statement statement = connection.createStatement(); + + //5.执行SQL可能存在的结果,查看返回的结果 + System.out.println("-------------------当前查询到的数据如下-------------------"); + ResultSet resultSet = statement.executeQuery("SELECT * FROM t1");//返回的结果集,封装了查询出来的全部结果 + while(resultSet.next()) { + System.out.println("id="+resultSet.getObject("id")); + System.out.println("name="+resultSet.getObject("NAME")); + } + + System.out.println("-------------------当前数据库的行数如下-------------------"); + ResultSet curCount = statement.executeQuery("select count(*) from t1"); + while(curCount.next()) { + System.out.println("当前数据库有数据 "+curCount.getInt(1)+" 行!"); + } + + System.out.println("-------------------向该数据库中插入数据-------------------"); + String insertSql = "insert into t1 values(3,'c')"; + statement.execute(insertSql);//返回的结果 + + System.out.println("-------------------插入数据后的行数如下-------------------"); + ResultSet afinCount = statement.executeQuery("select count(*) from t1"); + while(afinCount.next()) { + System.out.println("当前数据库有数据 "+afinCount.getInt(1)+" 行!"); + } + + System.out.println("-------------------尝试更新插入进去的数据-------------------"); + Integer updCount = statement.executeUpdate("update t1 set id=4 where name='c'"); + System.out.println("更新了 "+updCount+" 行数据!"); + + System.out.println("-------------------尝试进行数据删除-------------------"); + Boolean del = statement.execute("delete from t1 where name = 'c'"); + ResultSet afdelCount = statement.executeQuery("select count(*) from t1"); + while(afdelCount.next()) { + System.out.println("当前数据库有数据 "+afdelCount.getInt(1)+" 行!"); + } + + //6.释放连接 + resultSet.close(); + statement.close(); + connection.close(); + } +} +``` + +执行结果也一目了然,没问题: + +![](./media/dev-language/dev-java1.png) + +## 1.2 OB 自研 JDBC 连接 Demo + +OceanBase 官方提供了自研的 JDBC 包,名字叫做:oceanbase-client-1.1.10.jar。选用这个包的话,整体的操作方法跟 1.1 部分类似,**主要区别为驱动加载和 url 部分的定义**: + +```java + //1.加载驱动 + Class.forName("com.alipay.oceanbase.jdbc.Driver");//固定写法,加载驱动 + //2.用户信息和url + String url="jdbc:oceanbase://10.211.55.73:2883/test"; +``` + +按照惯例来说,官方自研的 JDBC 驱动在性能和语法兼容性上肯定会更好,当然我没有真正测过性能,大家可以自己测测试试。下面是一个完整 Demo 的增删改查代码: + +```java +import java.sql.*; + +public class JavaOBDemo { + public static void main(String[] args) throws ClassNotFoundException,SQLException { + //1.加载驱动 + Class.forName("com.alipay.oceanbase.jdbc.Driver");//固定写法,加载驱动 + //2.用户信息和url + String url="jdbc:oceanbase://10.211.55.73:2883/test"; + String username = "root"; + String password = "observer"; + //3.连接成功,连接数据库对象 Connection + Connection connection = DriverManager.getConnection(url,username,password); + //4.执行SQL对象 Statement + Statement statement = connection.createStatement(); + + //5.执行SQL可能存在的结果,查看返回的结果 + System.out.println("-------------------当前查询到的数据如下-------------------"); + ResultSet resultSet = statement.executeQuery("SELECT * FROM t1");//返回的结果集,封装了查询出来的全部结果 + while(resultSet.next()) { + System.out.println("id="+resultSet.getObject("id")); + System.out.println("name="+resultSet.getObject("NAME")); + } + + System.out.println("-------------------当前数据库的行数如下-------------------"); + ResultSet curCount = statement.executeQuery("select count(*) from t1"); + while(curCount.next()) { + System.out.println("当前数据库有数据 "+curCount.getInt(1)+" 行!"); + } + + System.out.println("-------------------向该数据库中插入数据-------------------"); + String insertSql = "insert into t1 values(3,'c')"; + statement.execute(insertSql);//返回的结果 + + System.out.println("-------------------插入数据后的行数如下-------------------"); + ResultSet afinCount = statement.executeQuery("select count(*) from t1"); + while(afinCount.next()) { + System.out.println("当前数据库有数据 "+afinCount.getInt(1)+" 行!"); + } + + System.out.println("-------------------尝试更新插入进去的数据-------------------"); + Integer updCount = statement.executeUpdate("update t1 set id=4 where name='c'"); + System.out.println("更新了 "+updCount+" 行数据!"); + + System.out.println("-------------------尝试进行数据删除-------------------"); + Boolean del = statement.execute("delete from t1 where name = 'c'"); + ResultSet afdelCount = statement.executeQuery("select count(*) from t1"); + while(afdelCount.next()) { + System.out.println("当前数据库有数据 "+afdelCount.getInt(1)+" 行!"); + } + + //6.释放连接 + resultSet.close(); + statement.close(); + connection.close(); + } +} +``` + +有关 OB 自研 JDBC 的参数调优,请参考官方文档:https://open.oceanbase.com/docs/observer-cn/V3.1.4/10000000000450598 + +## 1.3 对常用框架的支持 + +这也是个经常被问到的问题,我在社区群中不止一次看到有人问 OceanBase 对通用框架/连接池支持怎么样?大家想想,OB 在阿里内部及很多企业级客户都有很深入的应用了,对通用框架的支持已经不会是一个大问题了,**简言之:支持的很不错**。下面我列举几个官方给出的常用框架支持方式,供大家参考: + +### 1.3.1 MyBatis 连接示例 + +MyBatis 作为一款应用量排名靠前的框架,深受大家的喜爱。下面给出了一些常用的配置信息,供您参考: + +#### 配置依赖 + +```xml + + com.alipay.oceanbase + oceanbase-client + 3.2.3 + + + org.mybatis + mybatis + 3.5.4 + +``` + +#### mybatis-config.xml 配置文件 + +```xml + + + + + + + + + + + + + + + + + + + + +``` + +#### mapper.xml 配置文件 + +```xml + + + + + + + + delete from user; + + +``` + +### 1.3.2 HiKariCP 连接池配置示例 + +HiKariCP 以代码轻量且速度非常快著称,以下信息展示了 OceanBase 与该框架的集成方法: + +#### 配置依赖 pom.xml + +```xml + + com.alipay.oceanbase + oceanbase-client + 3.2.3 + + + com.zaxxer + HikariCP + 3.3.1 + +``` + +#### 配置文件 jdbc.properties + +```conf +jdbcUrl=jdbc:oceanbase://10.100.xxx.xxx:18817/test?useSSL=false&useServerPrepStmts=true&serverTimezone=UTC +username=admin@mysql +password=admin +dataSource.cachePrepStmts=true +dataSource.prepStmtCacheSize=250 +dataSource.prepStmtCacheSqlLimit=2048 +dataSource.useServerPrepStmts=true +dataSource.useLocalSessionState=true +dataSource.rewriteBatchedStatements=true +dataSource.cacheResultSetMetadata=true +dataSource.cacheServerConfiguration=true +dataSource.elideSetAutoCommits=true +dataSource.maintainTimeStats=false +``` + +#### 示例代码 + +```java + @Test + public void hikariTest() throws SQLException { + HikariConfig config = new HikariConfig("/jdbc.properties"); + Connection conn = new HikariDataSource(config).getConnection(); + PreparedStatement drop_table_data_test = conn.prepareStatement("drop table data_test"); + Statement stmt = conn.createStatement(); + try { + drop_table_data_test.execute(); + } catch (SQLException e) { + } + conn.prepareStatement("create table data_test(test1 int)").execute(); + for (int i = 0; i <= 10; i++) { + stmt.executeUpdate("insert into data_test values("+i+")"); + } + ResultSet rs = conn.prepareStatement("select * from data_test;").executeQuery(); + while (rs.next()){ + System.out.print(rs.getObject(1)+"\t"); + } + System.out.println(); + System.out.println("------------------------"); + stmt.executeUpdate("update data_test set test1 = 100;"); + ResultSet rs1 = conn.prepareStatement("select * from data_test;").executeQuery(); + while (rs1.next()){ + System.out.print(rs1.getObject(1)+"\t"); + } + System.out.println(); + System.out.println("------------------------"); + stmt.executeUpdate("delete from data_test"); + ResultSet rs2 = stmt.executeQuery("select * from data_test;"); + if (!rs2.next()){ + System.out.println("数据删除成功"); + } + stmt.close(); + drop_table_data_test.execute(); + drop_table_data_test.close(); + rs.close(); + conn.close(); + } +``` + +### 1.3.3 SpringJDBC 连接示例 + +SpringJDBC 是老牌的持久层框架了,下面也来一个简单的 Demo: + +#### 配置依赖 + +```xml + + com.alipay.oceanbase + oceanbase-client + 3.2.3 + + + org.springframework + spring-jdbc + 5.0.9.RELEASE + +``` + +#### 示例代码 + +预加载静态代码块(为方便测试,此处使用 druid 连接池): + +```java +static { + Map map = new HashMap(); + map.put("url", "jdbc:oceanbase://10.100.xxx.xxx:18815/test"); + map.put("driverClassName", "com.alipay.oceanbase.jdbc.Driver"); + map.put("username", "admin@mysql"); + map.put("password", "admin"); + try { + Class.forName(map.get("driverClassName")); + jdbcTemplate = new JdbcTemplate(DruidDataSourceFactory.createDataSource(map)); + //防止异常语句,没有这两句,会出错 + jdbcTemplate.execute("set transaction_isolation = 'READ-COMMITTED';"); + jdbcTemplate.execute("set tx_isolation = 'READ-COMMITTED';"); + } catch (Exception e) { + e.printStackTrace(); + } +} +``` + +```java + @Test + public void createByOrcTypeDate(){ + sql ="create table D_DPRECORD(DEV_ID VARCHAR2(50),"+ + "CAR_SPEED NUMBER(3),"+ + "CAP_DATE TIMESTAMP WITH LOCAL TIME ZONE," + + "DEV_CHNID VARCHAR2(50) not null," + + "TRSFMARK NUMBER(1) default 0," + + "CREATE_TIME DATE default sysdate" + + ");"; + jdbcTemplate.execute(sql); + } +``` + +```java + @Test + public void addTest(){ + int i = 1; + for (;i<=100;i++){ + sql = "insert into orc_type_test values ("+i+",'测试数据"+i+"');"; + jdbcTemplate.execute(sql); + } + } +``` + +```java + @Test + public void selectTest(){ + sql = "select * from orc_type_test;"; + List> maps = jdbcTemplate.queryForList(sql); + for (Map m : maps){ + System.out.println(m); + } + } +``` + +```java + @Test + public void rownumQueryTest() { + sql = "select * from D_DPRECORD where rownum <=70 minus (select * from D_DPRECORD where rownum<60);"; + List> maps = jdbcTemplate.queryForList(sql); + for (Map m : maps) { + System.out.println(m); + } + } +``` + +### 1.3.4 其他连接池和框架 + +这里引入介绍了部分内容,因为 Java 的连接池和框架太多了,所以为了保持篇幅不至于太冗长,不一一列举了,如果大家对其他内容感兴趣,欢迎参考官方文档的《设计规范和约束》--> 《数据库设计最佳实践》中的连接池和框架部分。 + +# 2. C/C++ 语言数据库连接驱动 + +OceanBase Connector/C 是一个基于 C/C++ 的 OceanBase 客户端开发组件,支持 C API Lib 库,我们通常也叫它 libobclient 。允许 C/C++ 程序以一种较为底层的方式访问 OceanBase 分布式数据库集群,以进行数据库连接、数据访问、错误处理和 Prepared Statement 处理等操作。当然您也可以使用 MySQL 官方的客户端开发组件:MySQL C API。但是优先推荐采用 libobclient 进行开发。 + +## 2.1 安装 libobclient + +在使用该库之前,我们需要先在对应的服务器上安装,例如执行如下命令,从 OceanBase 远程仓库安装,并复制一份到 lib64: + +```shell +sudo yum install libobclient +sudo cp -r /u01/obclient/lib/libobclnt.so.20 /usr/lib64/ +``` + +## 2.2 C++ 语言 Demo + +完成了上面的基础库安装后,下面我们还是通过一个例子来展示如何编写 C++ 代码: + +```c++ +#include +#include +#include +#include "mysql.h" + +using namespace std; + + +void process_result_set(MYSQL *mysql, MYSQL_RES *result) +{ + + printf("row nums: %d\n", mysql_num_rows(result)); //获取行数 + + int j = mysql_num_fields(result); + MYSQL_ROW row = mysql_fetch_row(result); + + // 输出每行结果 + if (row) { + for (int i = 0; i < j; i++) { + printf("%s \n", row[i]); + } + if (mysql_errno(mysql)) { + printf("retrive faile: %s\n", mysql_error(mysql)); + return; + } + } +} +int main() +{ + mysql_library_init(0, NULL, NULL); + MYSQL *mysql = mysql_init(NULL); + char host[] = "10.211.55.73"; + char user[] = "root"; + char passwd[] = "observer"; + char db[] = "test"; //没有数据库可以选择NULL + unsigned int port = 2883; + // 使用obclient连接ob的方式如下 + // obclient -h123.10.12.123 -P2883 -uroot@sys -pobserver + + /* 使用 CLIENT_MULTI_STATEMENTS 选项连接服务器 (必选)*/ + if (mysql_real_connect (mysql, host, user, passwd, + NULL, port, NULL, CLIENT_MULTI_STATEMENTS) == NULL) + { + printf("mysql_real_connect() failed\n"); + mysql_close(mysql); + mysql_library_end(); + exit(1); + } + + /*执行多条语句 */ + int status = mysql_query(mysql, + "show parameters like '%obconfig_url%';"); + + if (status) + { + printf("Could not execute statement(s), ret = %d\n", status); + mysql_close(mysql); + mysql_library_end(); + exit(0); + } + + /* 处理每个语句的结果 */ + do { + /* 当前语句是否返回数据? */ + MYSQL_RES* result = mysql_store_result(mysql); + if (result) + { + /* 是的; 则处理行并释放结果集 */ + process_result_set(mysql, result); // 处理执行成功的语句 + mysql_free_result(result); + } + else /* 没有结果集或报错 */ + { + if (mysql_field_count(mysql) == 0) + { + printf("%lld rows affected\n", + mysql_affected_rows(mysql)); + } + else /* 发生报错 */ + { + printf("Could not retrieve result set\n"); + break; + } + } + /* 是否有更多结果? -1 = 否,>0 = 报错,0 = 是(继续循环)*/ + if ((status = mysql_next_result(mysql)) > 0) + printf("Could not execute statement\n"); + } while (status == 0); + + mysql_close(mysql); + mysql_library_end(); + + return 0; +} +``` + +编译执行返回的结果如下: + +```shell +[chris@obd ~]$ g++ test.cpp -I /u01/obclient/include -L /u01/obclient/lib -lobclnt -o test +[chris@obd ~]$ ./test +row nums: 3 +zone3 +observer +10.211.55.76 +2882 +obconfig_url +(null) + +URL for OBConfig service +OBSERVER +CLUSTER +DEFAULT +DYNAMIC_EFFECTIVE +``` + +# 4. Python 连接数据库指南 + +Python 在很多场景下被大量使用,大家所熟知的 OceanBase Deployer,即 obd 也是用 Python 开发的。我们可以通过两个不同的库(Python 2 和 Python 3 有所区别)连接到 OceanBase。下面分别展开: + +## 4.1 Python 3 连接方式 + +官方推荐在 Python 3 环境下,选用 PyMySQL 库来连接 OceanBase,首先如果您本地没有下载该库,需要先执行 pip 进行安装: + +```shell +python3 -m pip install PyMySQL +``` + +安装完成后,可以通过如下代码进行数据库访问,主要就是注意 import 库的导入: + +```python +import pymysql + +conn = pymysql.connect(host="10.211.55.73", port=2883, + user="root", passwd="observer", db="test") + +try: + with conn.cursor() as cur: + cur.execute('SELECT * FROM t1') + rows = cur.fetchall() + for row in rows: + print(f'{row[0]} {row[1]}') + +finally: + conn.close() +``` + +执行如上代码返回结果也正常: + +![](./media/dev-language/dev-python1.png) + +## 4.2 Python 2 连接方式 + +MySQL-python 是 Python2.X 版本中用于连接 MySQL 服务器的一个库。官方推荐采用该库在 Python 2 上连接 OceanBase。 + +首先也是需要手工安装该库: + +```shell +sudo pip install MySQL-python +``` + +示例代码及返回结果如下: + +```python +import MySQLdb + +conn= MySQLdb.connect( + host='10.211.55.73', + port = 2883, + user='root', + passwd='observer', + db ='test' +) + +try: + cur = conn.cursor() + cur.execute('SELECT * from t1') + ans = cur.fetchall() + print(ans) + +finally: + conn.close() +``` + +```shell +[chris@obd ~]$ python test.py +((1L, 'a'), (2L, 'b')) +``` + +# 5. Golang 驱动链接 OceanBase Demo + +在 Golang 开发环境下,推荐采用 Go-SQL-Driver/MySQL 来进行连接。这要求 Go 的版本要在 1.13 之上。 + +## 5.1 下载对应的库 + +```shell +go get -u github.com/go-sql-driver/mysql +``` + +## 5.2 Demo 演示 + +```golang +package main + +import ( + "database/sql" + "fmt" + "log" + + _ "github.com/go-sql-driver/mysql" +) + +type TS struct { + Id int + Name string +} + +func select_all() { + conn := "root:obsever@tcp(10.211.55.73:2883)/test" + db, err := sql.Open("mysql", conn) + if err != nil { + log.Fatal(err) + } + + defer db.Close() + + if err != nil { + log.Fatal(err) + } + + res, err := db.Query("SELECT * FROM t1") + if err != nil { + log.Fatal(err) + } + + defer res.Close() + + if err != nil { + log.Fatal(err) + } + + for res.Next() { + + var t TS + err := res.Scan(&t.Id, &t.Name) + + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%v\n", t) + } +} + +func main() { + select_all() +} +``` + +执行结果截图如下,此处注意环境变量 GO111MODULE的设置,否则会去查找 mod 文件并导致无法执行: + +![](./media/dev-language/dev-golang1.png) + + + + + + +