各种语言如何连接到 OceanBase

发布于:2023-01-04 ⋅ 阅读:(914) ⋅ 点赞:(0)

本篇文章想跟大家分享一下,各种主流开发语言如何连接到 OceanBase 社区版数据库,进行日常开发。这个问题也是社区群里面问的比较多的一个问题之一,希望通过本篇文章,能给各位参与开发的小伙伴提供一些指导。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Al4Fkqqh-1661341617962)(./media/dev-language/dev-language-question.png)]

0. 前置条件

首先我们在 OceanBase test 数据库中创建一个 t1 表,并向里面插入 2 条数据,用作后面所有语言的测试基础表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OqV3cu53-1661341617963)(./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 代码执行增删改查操作,如下:

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();
    }
}

执行结果也一目了然,没问题:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-olj2UXsG-1661341617963)(./media/dev-language/dev-java1.png)]

1.2 OB 自研 JDBC 连接 Demo

OceanBase 官方提供了自研的 JDBC 包,名字叫做:oceanbase-client-1.1.10.jar。选用这个包的话,整体的操作方法跟 1.1 部分类似,主要区别为驱动加载和 url 部分的定义

 //1.加载驱动
 Class.forName("com.alipay.oceanbase.jdbc.Driver");//固定写法,加载驱动
 //2.用户信息和url
 String url="jdbc:oceanbase://10.211.55.73:2883/test";

按照惯例来说,官方自研的 JDBC 驱动在性能和语法兼容性上肯定会更好,当然我没有真正测过性能,大家可以自己测测试试。下面是一个完整 Demo 的增删改查代码:

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 作为一款应用量排名靠前的框架,深受大家的喜爱。下面给出了一些常用的配置信息,供您参考:

配置依赖

<dependency>
   <groupId>com.alipay.oceanbase</groupId>
   <artifactId>oceanbase-client</artifactId>
   <version>3.2.3</version>
</dependency>
<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis</artifactId>
   <version>3.5.4</version>
</dependency>

mybatis-config.xml 配置文件

<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.alipay.oceanbase.jdbc.Driver"/>
                <property name="url" value="jdbc:oceanbase://10.100.xxx.xxx:18817/test?useUnicode=true&amp;characterEncoding=utf-8&amp;useServerPrepStmts=false&amp;useCursorFetch=true"/>
                <property name="username" value="admin@mysql"/>
                <property name="password" value="admin"/>
            </dataSource>
        </environment>
    </environments>

    <!--注册mapper(mapper.xml所在地址)-->
    <mappers>
        <mapper resource="com/test/UserMapper.xml"></mapper>
    </mappers>
</configuration>

mapper.xml 配置文件

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace 是mapper接口,不能填错-->
<mapper namespace="com.test.UserMapper">
    <select id="selectUser" resultType="com.test.User" fetchSize="40000">
        select * from user;
    </select>

    <delete id="delete" >
        delete from user;
    </delete>
</mapper>

1.3.2 HiKariCP 连接池配置示例

HiKariCP 以代码轻量且速度非常快著称,以下信息展示了 OceanBase 与该框架的集成方法:

配置依赖 pom.xml

<dependency>
    <groupId>com.alipay.oceanbase</groupId>
    <artifactId>oceanbase-client</artifactId>
    <version>3.2.3</version>
</dependency>
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.3.1</version>
</dependency>

配置文件 jdbc.properties

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

示例代码

 @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:

配置依赖

<dependency>
   <groupId>com.alipay.oceanbase</groupId>
   <artifactId>oceanbase-client</artifactId>
   <version>3.2.3</version>
</dependency>
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>5.0.9.RELEASE</version>
</dependency>

示例代码

预加载静态代码块(为方便测试,此处使用 druid 连接池):

static {
        Map<String, String> map = new HashMap<String, String>();
        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();
        }
}
 @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);
    }
 @Test
    public void addTest(){
        int i = 1;
        for (;i<=100;i++){
            sql = "insert into orc_type_test values ("+i+",'测试数据"+i+"');";
            jdbcTemplate.execute(sql);
        }
    } 
 @Test
    public void selectTest(){
        sql = "select * from orc_type_test;";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        for (Map<String,Object> m : maps){
            System.out.println(m);
        }
    }
 @Test
    public void rownumQueryTest() {
        sql = "select * from D_DPRECORD where rownum <=70 minus (select * from D_DPRECORD where rownum<60);";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        for (Map<String, Object> 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:

sudo yum install libobclient
sudo cp -r /u01/obclient/lib/libobclnt.so.20 /usr/lib64/

2.2 C++ 语言 Demo

完成了上面的基础库安装后,下面我们还是通过一个例子来展示如何编写 C++ 代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#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;
}

编译执行返回的结果如下:

[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 进行安装:

python3 -m pip install PyMySQL

安装完成后,可以通过如下代码进行数据库访问,主要就是注意 import 库的导入:

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()

执行如上代码返回结果也正常:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F7uxBcSJ-1661341617964)(./media/dev-language/dev-python1.png)]

4.2 Python 2 连接方式

MySQL-python 是 Python2.X 版本中用于连接 MySQL 服务器的一个库。官方推荐采用该库在 Python 2 上连接 OceanBase。

首先也是需要手工安装该库:

sudo pip install MySQL-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()
[chris@obd ~]$ python test.py
((1L, 'a'), (2L, 'b'))

5. Golang 驱动链接 OceanBase Demo

在 Golang 开发环境下,推荐采用 Go-SQL-Driver/MySQL 来进行连接。这要求 Go 的版本要在 1.13 之上。

5.1 下载对应的库

go get -u github.com/go-sql-driver/mysql

5.2 Demo 演示

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 文件并导致无法执行:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7vTheO5w-1661341617964)(./media/dev-language/dev-golang1.png)]

本文含有隐藏内容,请 开通VIP 后查看