【MySQL】使用C/C++语言连接数据库

文章目录

  • [1. 安装 MySQL 库](#1. 安装 MySQL 库)
    • [1.1 官网下载](#1.1 官网下载)
    • [1.2 通过 yum 安装开发环境](#1.2 通过 yum 安装开发环境)
  • [2. 连接 MySQL](#2. 连接 MySQL)
  • [3. MySQL 接口介绍](#3. MySQL 接口介绍)
    • [3.1 初始化 mysql_init()](#3.1 初始化 mysql_init())
    • [3.2 链接数据库 mysql_real_connect](#3.2 链接数据库 mysql_real_connect)
    • [3.3 下发mysql命令 mysql_query](#3.3 下发mysql命令 mysql_query)
      • [3.3.1 解决插入中文的乱码问题](#3.3.1 解决插入中文的乱码问题)
    • [3.4 获取执行结果 mysql_store_result](#3.4 获取执行结果 mysql_store_result)
    • [3.5 获取结果行数 mysql_num_rows](#3.5 获取结果行数 mysql_num_rows)
    • [3.6 获取结果列数 mysql_num_fields](#3.6 获取结果列数 mysql_num_fields)
    • [3.7 获取列名 mysql_fetch_fields](#3.7 获取列名 mysql_fetch_fields)
    • [3.8 获取结果内容 mysql_fetch_row](#3.8 获取结果内容 mysql_fetch_row)
    • [3.9 释放执行结果 mysql_free_result](#3.9 释放执行结果 mysql_free_result)
    • [3.10 关闭mysql连接 mysql_close](#3.10 关闭mysql连接 mysql_close)
    • [3.11 事务常用操作](#3.11 事务常用操作)
      • [3.11.1 设置自动提交模式 mysql_autocommit](#3.11.1 设置自动提交模式 mysql_autocommit)
      • [3.11.2 提交当前事务 mysql_commit](#3.11.2 提交当前事务 mysql_commit)
      • [3.11.3 用于回滚当前事务 mysql_rollback](#3.11.3 用于回滚当前事务 mysql_rollback)

我们新建一个 edison 用户专门用来做 C/C++ 的测试。

sql 复制代码
# 创建用户
create user 'edison'@'localhost' identified by 'QAZwsxedc123456.';

# 查看用户表
select user,host,authentication_string from user;

# 刷新权限
flush privileges;

结果如下:

然后创建一个名为 ai_study_data 的数据库,并赋予 edison 用户对该数据库的所有权限:

sql 复制代码
# 建库
create database ai_study_data;

# 赋予权限
grant all on ai_study_data.* to 'edison'@'localhost';

# 刷新权限
flush privileges;

此时,当我们用 edison 用户登录以后,就可以看到 root 用户创建的数据库了

1. 安装 MySQL 库

这里提供两种做法,一是去官网下载,二是通过 yum 源安装。

1.1 官网下载

要使用 C/C++ 语言连接 MySQL,需要使用 MySQL 官网提供的库,可以去 官网 下载

然后点击箭头指向的【Connector/C++】

选择默认的 Connector/C++ 9.5.0 下载即可。

1.2 通过 yum 安装开发环境

但是咱们今天不推荐上面这种做法。

因为当初我们在安装 MySQL 的时候,执行的是下面这两条命令,实际上它已经把我们需要用到的【开发工具】给我们已经安装好了

sql 复制代码
# 安装MySQL客户端和服务器
[root@vm-centos:~]# yum install -y mysql-community-server

# 安装MySQL开发环境
[root@vm-centos:~]# yum install -y mysql-community-devel

注意:如果你第一次没有安装开发工具的话,需要去执行一下安装MySQL开发环境的命令。

其中 include 包含所有的方法声明,lib 包含所有的方法实现(打包成库)

bash 复制代码
[root@vm-centos:~]# ls /usr/include/mysql/

[root@vm-centos:~]# ls /lib64/mysql/libmysql*

如下所示:

其中:libmysqlclient.a 是一个静态库,libmysqlclient.so 是一个动态库,这两个库是和 /usr/include/mysql/ 目录下的头文件搭配使用的,可以让我们通过代码访问并连接到数据库。

2. 连接 MySQL

先尝试连接一下 MySQL Client,通过 mysql_get_client_info() 函数,来验证我们的引入是否成功。

编写 test.cc 函数

cpp 复制代码
#include <iostream>
#include <mysql/mysql.h>

using namespace std;

int main()
{
    cout << "mysql client Version: " <<  mysql_get_client_info() << endl;
    return 0;
}

当你在 VSCode 中引入头文件,如果给你自动提示时,说明引入成功了

编写 Makefile

bash 复制代码
test:test.cc
	g++ -o $@ $^ -L/lib64/mysql -lmysqlclient -std=c++11 
.PHONY:clean
clean:
	rm -f test

然后运行可执行文件 ./test 即可看到对应的 MySQL 版本:

其中:

  • -L/lib64/mysql: 这个选项指定了编译器搜索库文件的路径,/lib64/mysql 是 MySQL 客户端库所在的目录。编译器会在这个目录下查找所需的库文件。
  • -lmysqlclient: 这个选项告诉编译器将 libmysqlclient 库链接到程序中。libmysqlclient 是 MySQL 的客户端库,包含了与 MySQL 服务器进行交互所需的函数。

至此引入库的工作已经做完,接下来就是熟悉接口。

3. MySQL 接口介绍

MySQL 5.7 C API Developer Guide 上有关于 MySQL 的各种 API 接口,写的很全很详细,下面主要介绍一下常用的接口。

3.1 初始化 mysql_init()

要使用库,必须先进行初始化。

mysql_init() 是用于初始化一个 MySQL 连接句柄的函数。

c 复制代码
MYSQL *mysql_init(MYSQL *mysql);

参数:

  • mysql:这是一个指向 MYSQL 结构体的指针,表示要初始化的 MySQL 连接句柄。如果传入 NULL,mysql_init() 会自动为你分配内存。

返回值:

  • 如果初始化成功,返回一个指向 MYSQL 对象的指针。
  • 如果初始化失败,返回 NULL。

代码示例:

cpp 复制代码
int main()
{
    MYSQL *myfd = mysql_init(nullptr);
    if (nullptr == myfd)
    {
        cerr << "init MySQL error" << endl;
        return 1;
    }
    cout << "init MySQL Success" << endl;

    mysql_close(myfd);
    return 0;
}

结果如下:

3.2 链接数据库 mysql_real_connect

初始化完毕之后,必须先链接数据库,在进行后续操作(MySQL 网络部分是基于 TCP/IP 的)。

mysql_real_connect() 是用于实际建立与 MySQL 数据库服务器连接的函数。它使用指定的参数进行连接,并返回一个有效的 MySQL 连接句柄。

c 复制代码
MYSQL *mysql_real_connect(MYSQL *mysql, const char *host,
                          const char *user, const char *passwd,
                          const char *db, unsigned int port,
                          const char *unix_socket,
                          unsigned long client_flag);

参数说明:

  • mysql:一个指向 MYSQL 对象的指针,该对象需要先通过 mysql_init() 初始化。用于指定要连接的 MySQL 连接句柄。
  • host:数据库服务器的主机名或 IP 地址。如果连接本地数据库,可以使用 "localhost""127.0.0.1",也可以使用 UNIX 套接字文件路径(如果适用)。
  • user:数据库用户名,用于连接数据库的身份验证。
  • passwd:对应数据库用户的密码。
  • db:需要连接的数据库名称。如果连接成功,之后可以在该数据库中执行查询等操作。如果不想指定特定数据库,可以传入 NULL
  • port:MySQL 服务器的端口号,通常是 3306。如果使用 UNIX 套接字连接,此参数可以设置为 0
  • unix_socket:指定 UNIX 套接字文件的路径(如果连接到本地 MySQL 服务器并使用 UNIX 套接字)。如果不使用套接字连接,可以传入 NULL
  • client_flag:用于设置客户端连接的标志位。通常设置为 0,但可以使用一些选项(如 CLIENT_COMPRESS 等)来改变连接的行为。

返回值:

  • 如果连接成功,返回指向 MYSQL 连接对象的指针。
  • 如果连接失败,返回 NULL,并且可以通过 mysql_error() 获取详细的错误信息。

代码示例:

cpp 复制代码
// 下面的内容是在MySQL中创建的edison用户和测试数据库的信息
const string host = "127.0.0.1"; 
const string user = "edison";
const string passwd = "QAZwsxedc123456.";
const string db = "ai_study_data";
const unsigned int port = 3306;

int main()
{
    MYSQL *myfd = mysql_init(nullptr);
    if (nullptr == myfd)
    {
        cerr << "init MySQL error" << endl;
        return 1;
    }
    cout << "init MySQL Success" << endl;

    // 连接
    if (nullptr == mysql_real_connect(myfd, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0))
    {
        cerr << "connect MySQL error" << endl;
        return 2;
    }
    cout << "connect MySQL Success" << endl;

    mysql_close(myfd);
    return 0;
}

结果如下:

另外,我们可以在 MySQL 内部查看是否连接成功:

3.3 下发mysql命令 mysql_query

mysql_query() 是用于执行 SQL 查询的函数。

c 复制代码
int mysql_query(MYSQL *mysql, const char *q);

参数说明:

  • mysql:一个指向 MYSQL 对象的指针,表示已经建立的 MySQL 连接。
  • q:一个指向包含 SQL 查询语句的字符串。此字符串可以是任何有效的 SQL 查询,如 SELECT, INSERT, UPDATE, DELETE 等。

返回值:

  • 0:查询成功执行。
  • 非 0 值:查询执行失败,错误代码可以通过 mysql_error() 获取。

在编写代码前,我们先以 edison 用户的身份在 ai_study_data 数据库中建一张表

sql 复制代码
create table if not exists user(
id bigint primary key auto_increment,
name varchar(32) not null,
age int not null,
telephone varchar(32) unique
);

结果如下:

代码示例:

cpp 复制代码
int main()
{
    MYSQL *myfd = mysql_init(nullptr);
    if (nullptr == myfd)
    {
        cerr << "init MySQL error" << endl;
        return 1;
    }
    cout << "init MySQL Success" << endl;

    // 连接
    if (nullptr == mysql_real_connect(myfd, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0))
    {
        cerr << "connect MySQL error" << endl;
        return 2;
    }
    cout << "connect MySQL Success" << endl;

    // 下达指令
    string sql; // 定义sql语句
    while (true) // 模拟一个MySQL命令行操作
    {
        cout << "mysql> ";
        if (!getline(cin, sql) || sql == "quit") 
        {
            cout << "Bye bye" << endl;
            break;
        }
        int n = mysql_query(myfd, sql.c_str());
        if (0 == n)
            cout << "[" << sql << "] Operation success" << endl;
        else 
            cerr << "Operation failed: " << n << endl;
    } 
    mysql_close(myfd);
    return 0;
}

结果如下(在 C/C++ 里面编写 sql 的时候,可以不用带 ; 号):

但是,一情况下,在 C/C++ 调用 MySQL 的时候,不会是上面那种用法,而是直接帮我们执行 sql 语句即可。

代码示例:

cpp 复制代码
int main()
{
    MYSQL *myfd = mysql_init(nullptr);
    if (nullptr == myfd)
    {
        cerr << "init MySQL error" << endl;
        return 1;
    }
    cout << "init MySQL Success" << endl;

    // 连接
    if (nullptr == mysql_real_connect(myfd, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0))
    {
        cerr << "connect MySQL error" << endl;
        return 2;
    }
    cout << "connect MySQL Success" << endl;

    string sql = "update user set name='Jimmy' where id = 2"; // 定义sql语句
    int n = mysql_query(myfd, sql.c_str());
    if (0 == n)
        cout << "Operation success" << endl;
    else
        cerr << "Operation failed" << endl;
    
    // 关闭连接
    mysql_close(myfd);
    return 0;
}

结果如下:

同样,还可以进行插入操作

sql 复制代码
string sql = "insert into user (name, age, telephone) values ('Andy', 19, '987654321')"; 
int n = mysql_query(myfd, sql.c_str());
if (0 == n)
    cout << "Operation success" << endl;
else
    cerr << "Operation failed" << endl;

结果如下:

那么删除操作也是一样:

cpp 复制代码
string sql = "delete from user where id = 1";
int n = mysql_query(myfd, sql.c_str());
if (0 == n)
    cout << "Operation success" << endl;
else
    cerr << "Operation failed" << endl;

结果如下:

3.3.1 解决插入中文的乱码问题

如果我们执行插入中文字符的 sql 语句会怎么样呢?

cpp 复制代码
string sql = "insert into user (name, age, telephone) values ('张三', 20, '555333999')";
int n = mysql_query(myfd, sql.c_str());
if (0 == n)
    cout << "Operation success" << endl;
else
    cerr << "Operation failed" << endl;

结果如下:

可以看到,获取英文没有问题,但是获取中文却是乱码,那么我们需要设置连接的默认字符集是 utf8(原始默认的是 latin1)。

cpp 复制代码
mysql_set_character_set(myfd, "utf8");

再次插入带中文字符的 sql

cpp 复制代码
int main()
{
    MYSQL *myfd = mysql_init(nullptr);
    if (nullptr == myfd)
    {
        cerr << "init MySQL error" << endl;
        return 1;
    }
    cout << "init MySQL Success" << endl;

    // 连接
    if (nullptr == mysql_real_connect(myfd, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0))
    {
        cerr << "connect MySQL error" << endl;
        return 2;
    }
    mysql_set_character_set(myfd, "utf8"); // 设置连接字符
    cout << "connect MySQL Success" << endl;
    
    string sql = "insert into user (name, age, telephone) values ('王五', 21, '111222333')";
    int n = mysql_query(myfd, sql.c_str());
    if (0 == n)
        cout << "Operation success" << endl;
    else
        cerr << "Operation failed" << endl;
    
    // 关闭连接
    mysql_close(myfd);
    return 0;
}

可以看到能够支持中文字符了

(记得把乱码的记录给删除掉)

3.4 获取执行结果 mysql_store_result

编写的 sql 执行完以后,如果是查询语句,我们当然还要读取数据,如果 update / insert 等语句,那么就只需要看下操作成功与否即可。 如果 mysql_query 返回成功,那么我们就通过下面这个函数来读取结果。

mysql_store_result() 是用于检索 SQL 查询结果集的函数。

c 复制代码
MYSQL_RES *mysql_store_result(MYSQL *mysql);

参数说明:

  • mysql:一个指向 MYSQL 对象的指针,表示已经建立的 MySQL 连接。

返回值:

  • 返回一个指向 MYSQL_RES 结果集的指针。
  • 如果查询没有结果或发生错误,返回 NULL

代码示例:

cpp 复制代码
// 获取查询到的结果集
MYSQL_RES *res = mysql_store_result(myfd);
if (nullptr == res)
{
    cerr << "mysql store result failed" << endl;
    return 4;
}

3.5 获取结果行数 mysql_num_rows

mysql_num_rows() 是用于获取查询结果集中的行数的函数。

c 复制代码
my_ulonglong mysql_num_rows(MYSQL_RES *res);

参数说明:

  • res:一个指向 MYSQL_RES 结构体的指针,表示查询结果集。

返回值:

  • 返回结果集中的行数。
  • 如果查询失败或没有返回任何行,返回 0

3.6 获取结果列数 mysql_num_fields

mysql_num_fields() 是用于获取查询结果集中的列数的函数。

c 复制代码
unsigned int mysql_num_fields(MYSQL_RES *res);

参数说明:

  • res:一个指向 MYSQL_RES 结构体的指针,表示查询结果集。

返回值:

  • 返回结果集中的列数。
  • 如果查询失败或没有返回任何列,返回 0

接下来,我们可以通过上面两个函数来打印验证结果集中的【行 / 列】。

代码示例:

cpp 复制代码
int main()
{
    MYSQL *myfd = mysql_init(nullptr);
    if (nullptr == myfd)
    {
        cerr << "init MySQL error" << endl;
        return 1;
    }
    cout << "init MySQL Success" << endl;

    // 连接
    if (nullptr == mysql_real_connect(myfd, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0))
    {
        cerr << "connect MySQL error" << endl;
        return 2;
    }
    mysql_set_character_set(myfd, "utf8");
    cout << "connect MySQL Success" << endl;

		// 操作mysql
    string sql = "select * from user";
    int n = mysql_query(myfd, sql.c_str());
    if (0 == n)
        cout << "Operation success" << endl;
    else
    {
        cerr << "Operation failed" << endl;
        return 3;
    }

    // 获取查询到的结果集
    MYSQL_RES * res = mysql_store_result(myfd);
    if (nullptr == res)
    {
        cerr << "mysql store result failed" << endl;
        return 4;
    }

    // 
    int rows = mysql_num_rows(res);
    int fields = mysql_num_fields(res);
    cout << "行: " << rows << endl;
    cout << "列: " << fields << endl;
    
    // 关闭连接
    mysql_close(myfd);
    return 0;
}

结果如下:

3.7 获取列名 mysql_fetch_fields

mysql_fetch_fields() 是用于获取查询结果集中的所有列信息的函数。

c 复制代码
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);

参数说明:

  • res:一个指向 MYSQL_RES 结构体的指针,表示查询结果集。

返回值:

  • 返回一个指向 MYSQL_FIELD 结构体数组的指针,数组中的每个元素表示结果集中的一列。
  • 如果查询失败或没有列,返回 NULL

提取结果集中的列属性(其实就是打印 user 表中每一列的名称)代码示例:

cpp 复制代码
int main()
{
    MYSQL *myfd = mysql_init(nullptr);
    if (nullptr == myfd)
    {
        cerr << "init MySQL error" << endl;
        return 1;
    }
    cout << "init MySQL Success" << endl;

    // 连接
    if (nullptr == mysql_real_connect(myfd, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0))
    {
        cerr << "connect MySQL error" << endl;
        return 2;
    }
    mysql_set_character_set(myfd, "utf8");
    cout << "connect MySQL Success" << endl;

    // 对表执行查询操作
    string sql = "select * from user";
    int n = mysql_query(myfd, sql.c_str());
    if (0 == n)
        cout << "Operation success" << endl;
    else
    {
        cerr << "Operation failed" << endl;
        return 3;
    }

    // 获取查询到的结果集
    MYSQL_RES * res = mysql_store_result(myfd);
    if (nullptr == res)
    {
        cerr << "mysql store result failed" << endl;
        return 4;
    }

    // 下面全是对res结果集进行操作
    int rows = mysql_num_rows(res);
    int fields = mysql_num_fields(res);
    cout << "行: " << rows << endl; 
    cout << "列: " << fields << endl;

    // 获取表的属性
    MYSQL_FIELD *fields_array = mysql_fetch_fields(res);
    for (int i = 0; i < fields; i ++)
    {
        cout << fields_array[i].name << "\t";
    }
    cout << "\n";
    
    // 关闭连接
    mysql_close(myfd);
    return 0;
}

可以看到,此时就打印出了表中每一列的名称:

另外,还可以通过下面的代码,来查看我们当前用的是哪个数据库,以及库里面的哪张表?

cpp 复制代码
cout << fields_array[0].db << " : " << fields_array[0].table << endl;

结果如下:

3.8 获取结果内容 mysql_fetch_row

mysql_fetch_row() 是用于从查询结果集中获取一行数据的函数。

c 复制代码
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);

参数说明:

  • result:一个指向 MYSQL_RES 结构体的指针,表示查询结果集。

返回值:

  • 返回一个 MYSQL_ROW 类型的指针,表示结果集中的一行数据。每一行数据是一个字符串数组,数组中的每个元素对应一列的值。
  • 如果没有更多的行或查询失败,返回 NULL

提取结果集中每一行的内容(其实就是打印 user 表中所有的数据),代码示例:

cpp 复制代码
int main()
{
    MYSQL *myfd = mysql_init(nullptr);
    if (nullptr == myfd)
    {
        cerr << "init MySQL error" << endl;
        return 1;
    }
    cout << "init MySQL Success" << endl;

    // 连接
    if (nullptr == mysql_real_connect(myfd, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0))
    {
        cerr << "connect MySQL error" << endl;
        return 2;
    }
    mysql_set_character_set(myfd, "utf8");
    cout << "connect MySQL Success" << endl;

    string sql = "select * from user";
    int n = mysql_query(myfd, sql.c_str());
    if (0 == n)
        cout << "Operation success" << endl;
    else
    {
        cerr << "Operation failed" << endl;
        return 3;
    }

    // 获取查询到的结果集
    MYSQL_RES * res = mysql_store_result(myfd);
    if (nullptr == res)
    {
        cerr << "mysql store result failed" << endl;
        return 4;
    }

    // 下面全是对res结果集进行操作
    int rows = mysql_num_rows(res);
    int fields = mysql_num_fields(res);
    cout << "行: " << rows << endl; 
    cout << "列: " << fields << endl;

    // 
    for (int i = 0; i < rows; i ++)
    {
        MYSQL_ROW row = mysql_fetch_row(res); // 每一行的数据
        for (int j = 0; j < fields; j ++)
        {
            cout << row[j] << "\t";
        }
        cout << endl;
    }
    
    // 关闭连接
    mysql_close(myfd);
    return 0;
}

可以看到,此时就能拿到表的属性(每一列的名称)和所有数据了:

3.9 释放执行结果 mysql_free_result

mysql_free_result() 是用于释放由 mysql_store_result()mysql_use_result() 获取的结果集的函数。

c 复制代码
void mysql_free_result(MYSQL_RES *result);

参数说明:

  • result:一个指向 MYSQL_RES 结构体的指针,表示要释放的查询结果集。

返回值:

  • 无返回值。

代码示例:

cpp 复制代码
// 释放结果集res
mysql_free_result(res);

3.10 关闭mysql连接 mysql_close

mysql_close() 是用于关闭 MySQL 连接的函数。它释放与 MySQL 连接相关的资源并关闭连接。

cpp 复制代码
void mysql_close(MYSQL *sock);

参数:

  • sock:这是一个指向已建立的 MYSQL 连接的指针,表示要关闭的 MySQL 连接。该指针通常是通过 mysql_init()mysql_real_connect() 创建的连接。

返回值:

  • 该函数没有返回值。

3.11 事务常用操作

mysql_autocommit()mysql_commit()mysql_rollback() 是用于事务管理的函数。

3.11.1 设置自动提交模式 mysql_autocommit

c 复制代码
my_bool STDCALL mysql_autocommit(MYSQL *mysql, my_bool auto_mode);

参数说明:

  • mysql:一个指向 MYSQL 对象的指针,表示已建立的 MySQL 连接。
  • auto_mode:一个布尔值,表示是否启用自动提交模式。如果为 TRUE,则每个 SQL 语句都会自动提交;如果为 FALSE,则需要显式调用 mysql_commit()mysql_rollback() 来提交或回滚事务。

返回值:

  • 如果设置成功,返回 TRUE
  • 如果失败,返回 FALSE

3.11.2 提交当前事务 mysql_commit

c 复制代码
my_bool STDCALL mysql_commit(MYSQL *mysql);

参数说明:

  • mysql:一个指向 MYSQL 对象的指针,表示已建立的 MySQL 连接。

返回值:

  • 如果提交成功,返回 TRUE
  • 如果失败,返回 FALSE

3.11.3 用于回滚当前事务 mysql_rollback

c 复制代码
my_bool STDCALL mysql_rollback(MYSQL *mysql);

参数说明:

  • mysql:一个指向 MYSQL 对象的指针,表示已建立的 MySQL 连接。

返回值:

  • 如果回滚成功,返回 TRUE
  • 如果失败,返回 FALSE
相关推荐
高一要励志成为佬2 小时前
【数据库】第二章 关系数据库 各种概念
数据库
youshang520i2 小时前
Gbase 8s 不能参考一个未设日志的外在数据库
数据库
宇宙的尽头是PYTHON2 小时前
开窗函数简单的排序row_number() rank() densite()
数据库
IT教程资源D2 小时前
[N_096]基于springboot高校社团管理系统
mysql·springboot社团管理·社团管理系统
心止水j2 小时前
hbase 电商2
数据库·windows·hbase
CodeOfCC2 小时前
c语言 封装跨平台条件变量头文件
c语言
Blockbuater_drug3 小时前
SDF 格式文件的前世今生:从化学信息学基石到 AI 时代的分子通用语言
数据库·人工智能·化学信息学·sdf格式
_Voosk3 小时前
macOS Xcode C++程序设置相对路径根目录
c语言·c++·xcode·swift
Chloeis Syntax3 小时前
MySQL初阶学习日记(7)--- 事务
java·数据库·笔记·学习·mysql