mysql访问
- 1.引入MySQL 客户端库
- 2.C/C++ 进行增删改
- 3.查询的处理细节
- 4.图形化界面访问数据库
- 4.1下载MYSQL Workbench
- 4.2MYSQL Workbench远程连接数据库
点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃
1.引入MySQL 客户端库
从开始到选择我们用的都是命令行式的mysql访问mysqld,向mysqld下达我们的指令,其实在数据库层面上,连接数据库的客户端除了现在命令行式的客户端,还有图形化界面、网页版的,当然也包括语言级别的库或者包帮我们去访问数据库。
下面我们就用C/C++访问数据库。关于访问数据库我们要做两个准备工作,一是创建一个远端或者本地访问的请求也就是创建一个专门用来进行用C/C++访问客户端的账号。
reate user 'connector'@'localhost' identified by 'xxx';
然后创建一个数据库,赋予这个账号对数据库中表的所有权限。
二是安装C/C++要能访问的库,这个库有两种准备,第一种是从官网下载库,
下面这个是用不同语言连接MYSQL,官方提供的库。
下载推荐的8.0
我们用的是linux,所以选择linux系统x86 64位
下载好,上传到linux,然后解压一下,mysql-connector是解压后重命名的
这是解压后的文件,最重要的就是include 头文件,还有一个lib64 库
未来头文件给我们提供连接mysql的方法,库 提供连接mysql的库函数
我们就可以根据头文件,直接调用头文件的的方法,在编译连接的时候把库连入进来。如果忘记怎么引入,在Linux哪里基础IO动静态库就有对应的方法!
但是这种方式不推荐了!之间不是下载mysql,用yum源下载吗。yum源它会给我们找到合适的服务器,客户端,甚至是开发包。所以我们直接在yum哪里去找进行了。
没下载过mysql 可以下一下如果你安装过yum对应msyql源的话,如果不会下请移步于
【MySQL】MySQL在 Linux下环境安装
yum install -y mysql-community-server
如果你下载过,你就可以看到这里有对应的头文件,未来我们主要用的就是mysql.h
如果你下载过但是找不到头文件,执行下面的命令,就可以看到了
install -y mysql-devel
mysql库在系统默认安装路径下
开发环境我们有已经准备好了,接下来我们学习具体的接口。不过我们通过 mysql_get_client_info() 函数,来验证我们的引入是否成功。
#include <iostream>
#include <mysql/mysql.h>
using namespace std;
int main()
{
//获取当前客户端版本信息
cout<<"mysql client Version: "<<mysql_get_client_info()<<endl;
return 0;
}
如果直接编译肯定会报错,说的是未定义的引用,也就是这个mysql_get_client_info()找不到。
之前基础IO就说过,你要保证这个外来库能被正确连接,gcc/g++默认会选择C和C++的库,虽然你的mysql在系统中,系统也会默认会去/lib64路径下去找,
但是现在的问题是我怎么知道我需要连接那个库,尽管能找到这个库,但是我怎么知道我们该连那个库呢?所以我们需要需要在编译的时候就指明需要连那个库,
g++ -o mytest test.cc -L/lib64/mysql -lmysqlclient
然后就可以打出来所有mysql客户端的版本,说明这个库就被重新引入了
以前引入外来库不是还带上面-I选项,今天这里咋没有带。这是因为g++默认会去include里去找。然后拿着代码里的
去找,所有不会报头文件找不到的问题。如果你非得带-I,就把前面去掉
g++ -o mytest test.cc -I/usr/include/mysql -L/lib64/mysql -lmysqlclient
接下来我们我们通过这个库它内部给我提供对应的方法来让我们连接mysqld服务端,然后进行访问。
2.C/C++ 进行增删改
接下来我们先认识mysql常用接口。msyql接口介绍
这里还有mysql库为了方便操作mysql,然后提前在库中给我创建很多的数据结构
msyql的是一套网络服务,就注定了实际在进行mysql操作之前比如下达sql,一定在之前要连接mysql,而在连接mysql之前,我们还要先来创建一下mysql基础数据结构。要使用库,必须先进行初始化。mysql_init()帮我们创建一个MYSQL对象
MYSQL *mysql_init(MYSQL *mysql);
成功返回MYSQL对象,失败返回nullptr,用的时候必须要调用mysql_close()对应的链接。
MYSQL是 C api中一个非常重要的变量(mysql_init的返回值),里面内存非常丰富,有port,dbname,charset等连接基本参数。它也包含了一个叫 st_mysql_methods的结构体变量,该变量里面保存着很多函数指针,这些函数指针将会在数据库连接成功以后的各种数据操作中被调用。
MYSQL实际上就是一个结构体里面包含连接mysql相关保存好的一些属性。这相当于FILE*,用来标识mysql客户端一些资源,我们把这些东西可以称之为句柄。
#include <iostream>
#include <mysql/mysql.h>
using namespace std;
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL* my=mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
把数据库对应的MYSQL结构体对象创建初始化好,最终不用它了把它释放。可是要访问mysql之前要先连接数据库,连接数据库的函数。
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 clientflag);
第一个参数就是刚才创建的MYSQL对象。连接成功后把连接成功套接字信息保存到MYSQL对象中。然后要连接的mysql的主机在哪里,用户是谁,密码是多少,连接那个数据库,数据库的端口号是多少,剩下设置为nullptr,0就可以了。
成功时把我们传入的MYSQL给我们返回,失败返回nullptr。
最开始创建的只允许在本地主机连接数据库的用户就用上了。
#include <iostream>
#include <mysql/mysql.h>
#include <string>
using namespace std;
const string host="localhost"; //host="127.0.0.1" 也是可以的
const string user="connector";
const string passwd="xxx";
const string db="conn";
unsigned int port=xxx; //自己的端口号不要暴露
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL* my=mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if(mysql_real_connect(my,host.c_str(),user.c_str(),passwd.c_str(),db.c_str(),port,nullptr,0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
cout<<"connect success"<<endl;
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
自此我们完成了连接mysql的第工作
- 初始化mysql,构建mysql对象,得到mysql访问句柄
- 进行mysql 的connect连接
- 用完mysql之后close
然后就可以对mysql下达sql指令了。这个下达sql指令的函数是mysql_query对数据库下达指令。第一个参数就是刚才的MYSQL对象,第二个参数就是下达的sql指令。以前我们在命令行写单sql后面会带;,但是在这里并不需要带;or \g,成功时返回0,失败时返回非0。
在此之前可以自己先创建一个表
create table user(
id bigint primary key auto_increment,
name varchar(32) not null,
age int not null,
telphone varchar(32) unique
);
之前我们学过一个sql指令,查看正在连接数据库的用户
show processlist;
可以看到我们C/C++库确实连接到数据库了
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
// cout<<"connect success"<<endl;
string sql;
while (true)
{
cout << " mysql> ";
if (!getline(cin, sql) || sql == "quit")
{
cout << "Bye" << endl;
break;
}
int n = mysql_query(my, sql.c_str());
if (n == 0)
{
cout << sql << " success" << endl;
}
else
{
cerr << sql << " error" << endl;
return 3;
}
}
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
这里名字我们先输入英文,具体原因等会说。下达的这个sql执行可带;或者不带;。然后看到我们确实把信息插入到数据库了。我们其实也可以自己搞一个mysql的客户端。但是实际上我们也不会这样做,别人自己有客户端。我们直接向数据库下达sql指令就行了。
更新
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
string sql="update user set name='jim' where id=2";
int n=mysql_query(my,sql.c_str());
if(n == 0) cout<<sql<<" success"<<endl;
else cout<<sql<<" error"<<endl;
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
执行一下,看到id=2的名字确实更新为jim了。
所以未来我们所写的任何的关于数据库方面的程序,你想使用数据库,你可以提前建好数据库账号然后赋权,并且建表。然后编写对应C/C++代码。然后就可以为你的上层进行数据层面上的支撑了。
插入
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
//string sql="update user set name='jim' where id=2";
string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";
int n=mysql_query(my,sql.c_str());
if(n == 0) cout<<sql<<" success"<<endl;
else cout<<sql<<" error"<<endl;
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
删除
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
//string sql="update user set name='jim' where id=2";
//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";
string sql="delete from user where id=1";
int n=mysql_query(my,sql.c_str());
if(n == 0) cout<<sql<<" success"<<endl;
else cout<<sql<<" error"<<endl;
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
单sql本身就是事务,mysql默认事务提交是自动的,所以今天你可以用一个连接访问数据库,也可以创建十个连接访问数据库,你可以用命令行方式访问数据库,也可以用C/C++访问数据库。所以数据库可以存在多个访问,因为有事务,事务有ACID能保证原子性等等。所以我们在应用层就不用担心业务代码执行时出现问题。
mysql_query这个函数现在可以增删改,那查行不行呢?
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
//string sql="update user set name='jim' where id=2";
//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";
//string sql="delete from user where id=1";
string sql="select * from user";
int n=mysql_query(my,sql.c_str());
if(n == 0) cout<<sql<<" success"<<endl;
else cout<<sql<<" error"<<endl;
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
别人告诉我成功了啊,然后呢?
在我们数据库语句中,增删改是最简单的,因为增删改只需要保证成功即可,那么增删改操作一定能完成。而select是最不好处理的,因为当成功执行select语句,只是执行完sql第一步,那select后的数据在那呢?你是不是要把数据给显示处理或者说让用户获取到,所以还需要在做后面的工作。所以select还有后序获取数据包括把数据在交给它的调用层。而不像增删改执行完就完了。
还有一个问题,刚刚在插入的时候,name插入都是英文,那我们插入中文会怎么样呢?
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
//string sql="update user set name='jim' where id=2";
//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";
string sql="insert into user (name,age,telphone) values ('张三',18,11187654321)";
//string sql="delete from user where id=1";
//string sql="select * from user";
int n=mysql_query(my,sql.c_str());
if(n == 0) cout<<sql<<" success"<<endl;
else cout<<sql<<" error"<<endl;
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
我们插入中文时乱码了!
乱码问题怎么解决?我们的数据库编码格式都配过是utf8的。出现乱码问题,一定是因为客户端和服务端对编码问题没有形成一致。比如说服务端用的是utf8,客户端用的是latin1。因为我们数据库编码都是utf8的,所以一定是客户端出现了问题。
建立好链接之后,获取英文没有问题,如果获取中文是乱码:设置链接的默认字符集是utf8,原始默认是latin1
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
// 设置编码格式
mysql_set_character_set(my, "utf8");
//string sql="update user set name='jim' where id=2";
//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";
string sql="insert into user (name,age,telphone) values ('李四',18,12187654321)";
//string sql="delete from user where id=1";
//string sql="select * from user";
int n=mysql_query(my,sql.c_str());
if(n == 0) cout<<sql<<" success"<<endl;
else cout<<sql<<" error"<<endl;
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
设置好编码,在插入中文就没问题了。
自此我们就已经能够进行mysql对象初始化,mysql的连接,mysql的关闭,以及通过mysql_query向数据库下达对应的指令了。下面我们就解决拿到select的数据问题。
3.查询的处理细节
查sql本身能够被正确执行,但是执行之后怎么把查的数据交给上层返回给客户或者把它的数据显示处理呢?那在执行select的时候要把select的结果通过mysql_query查完之后把结果应该让我们怎么获取到呢?实际上mysql_query在执行select的时候会把结果保存到MYSQL这个句柄中。这个句柄是一个结构体它本身里面会包含对应的保存数据的缓存区区域。但是这部分数据依旧要被我们提取出来的。我们通过mysql_store_result这个函数来将结果进行转储。这个函数提取并且转储到结果集中。会把结果按照条目放置好。
是从MYSQL这个句柄中把结果转储到MYSQL_RES这个结构中。
把查询结果按照行为单位,全部都放进结果集中。
成功返回MYSQL_RES对象,失败返回nullptr。也可以采用mysql_errno和mysql_error来进行判断出错原因。
下面我们看看怎么用这个函数
首先先将结果转储到MYSQL_RES结构体中。
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
// cout<<"connect success"<<endl;
// 设置编码格式
mysql_set_character_set(my, "utf8");
string sql="select * from user";
int n=mysql_query(my,sql.c_str());
if(n == 0)
{
cout<<sql<<" success"<<endl;
}
else
{
cout<<sql<<" error"<<endl;
return 3;
}
//转储
MYSQL_RES* res=mysql_store_result(my);
if(res == nullptr)
{
cerr<<"mysql_store_result error"<<endl;
return 4;
}
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
那MYSQL_RES这个结构体应该如何理解呢?
首先我们要知道我们查出来的是一种表结构,表结构中行列关系是我们打印出来的,尤其是这些分割符,这些东西我们自己打印出来的,并不是在mysql在表中真实存在的。
在mysql表中其实存在两类信息,一类是存储表结构中的列属性或者列名称,还有一类是存储表中的内容。
我们先看内容,这些内容以一定格式先放到MYSQL结构体对象内部,然后在转储到MYSQL_RES结构中。转储是为了更好的便于做二次处理。
在表中查出来的数据,就一定是在MYSQL内部在内存中保存这部分内容的。mysql将所有数据,读取出来的时候,全部都当作字符串。 虽然以前它们什么int,char等,但是一旦把数据读到自定义缓存区中的时候,那么它一定是把所有数据都当作字符串来看待。以前约束类型只是在插入的时候要处理,读取出来当当作字符串处理。
然后MYSQL_RES要把结果归置一下,我可以把MYSQL_RES想象成一个存着char**的指针数组。数组大小代表筛选出来的数据记录有多少行。一条记录里面有多列,每一列都是字符串。
然后这个数组中每一个char** 指针都指向一个存访char类型的指针数组,指向的是这个数组中首个元素的地址。char** 的指针数组大小代表的是数据记录有多少行,而每一行char 的指针数组里的char* 依次指向一条记录的每一列。
所以未来想提取一整行,想提取一整行中的列,我可以按照行列去提取!
我们可以把MYSQL_RES当作一个char**xx[]数组去理解,或者当作一个char*[][]数组去理解都可以,怎么方便怎么来。纵向去遍历每一行,横向去遍历一行中每一列。就可以找所有查询结果都找到。
未来MYSQL_RES就以这样的方式把查询的结果保存起来。
那结果最终怎么遍历呢?首先我们要知道这个转储的结果有多少行,有多少列!然后按照行列去遍历。
获取结果行数mysql_num_rows
参数是MYSQL_RES,从MYSQL_RES结果集中拿结果有多少行。
my_ulonglong mysql_num_rows(MYSQL_RES *res);
获取结果列数mysql_num_fields
unsigned int mysql_num_fields(MYSQL_RES *res);
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
// cout<<"connect success"<<endl;
// 设置编码格式
mysql_set_character_set(my, "utf8");
string sql="select * from user";
int n=mysql_query(my,sql.c_str());
if(n == 0)
{
cout<<sql<<" success"<<endl;
}
else
{
cout<<sql<<" error"<<endl;
return 3;
}
MYSQL_RES* res=mysql_store_result(my);
if(res == nullptr)
{
cerr<<"mysql_store_result error"<<endl;
return 4;
}
my_ulonglong row=mysql_num_rows(res);
my_ulonglong col=mysql_num_fields(res);
cout << "行: " << rows << endl;
cout << "列: " << cols << endl;
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
目前这个表不就是3行4列吗
那结果怎么拿?遍历每一行每一列。一行一列的把结果都拿到。
mysql为了很好的支持遍历,它内部还提供一种数据结构叫做MYSQL_ROW,每次拿到一行。
获取结果内容mysql_fetch_row
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
这个函数就向以前学过的迭代器,当我们第一次调用mysql_fetch_row它内部会为我们维护当前遍历的第一行,把第一行的结果集以MYSQL_ROW形式返回。当遍历第二行的时候,只需要在调用一次mysql_fetch_row,不用自己维护行下标,它会自动指向下一行。就如迭代器一样,只需要按照特定的次数进行遍历调用就会依次把元素遍历一遍,特别像迭代器。
可以看到MYSQL_ROW其实就是一个char指针,因为只有char才能指向第一行第二行等等。
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
// cout<<"connect success"<<endl;
// 设置编码格式
mysql_set_character_set(my, "utf8");
string sql = "select * from user";
int n = mysql_query(my, sql.c_str());
if (n == 0)
{
cout << sql << " success" << endl;
}
else
{
cout << sql << " error" << endl;
return 3;
}
MYSQL_RES *res = mysql_store_result(my);
if (res == nullptr)
{
cerr << "mysql_store_result error" << endl;
return 4;
}
//下面都是和结果集有关的, MYSQL_RES
my_ulonglong rows = mysql_num_rows(res);
my_ulonglong cols = mysql_num_fields(res);
cout << "行: " << rows << endl;
cout << "列: " << cols << endl;
//拿结果
for(int i=0;i<rows;++i)
{
MYSQL_ROW line=mysql_fetch_row(res);
for(int j=0;j<cols;++j)
{
cout<<line[j]<<"\t";
}
cout<<endl;
}
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
现在我们就可以把select的结果都拿到了!
刚才说了在获取msyql表内容的时候,我们除了获取表格中内容,它的列属性我们也想获取。想知道它是那一列的,或者想知道当前查的是那张表,那个数据库。怎么办呢?我们有另一个接口。
获取列名mysql_fetch_fields
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);
与mysql_fetch_fields相对的还有一个mysql_fetch_field接口,mysql_fetch_fields以数组形式返回的是所有的列属性对应的结构体。每一列属性都是一个结构体。
mysql_fetch_field可以让你依次遍历所有列,以列为单位,第一次是第一列,第二次是第二列,也就像迭代器。
这里我们就不麻烦了,直接一次获得所有列属性。然后可以遍历所有列把列名称打印出来。
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
// cout<<"connect success"<<endl;
// 设置编码格式
mysql_set_character_set(my, "utf8");
string sql = "select * from user";
int n = mysql_query(my, sql.c_str());
if (n == 0)
{
cout << sql << " success" << endl;
}
else
{
cout << sql << " error" << endl;
return 3;
}
MYSQL_RES *res = mysql_store_result(my);
if (res == nullptr)
{
cerr << "mysql_store_result error" << endl;
return 4;
}
//下面都是和结果集有关的, MYSQL_RES
my_ulonglong rows = mysql_num_rows(res);
my_ulonglong cols = mysql_num_fields(res);
cout << "行: " << rows << endl;
cout << "列: " << cols << endl;
//属性
MYSQL_FIELD* fields=mysql_fetch_fields(res);
for(int i=0;i<cols;++i)
{
cout<<fields[i].name<<"\t";
}
cout<<endl;
//内容
for(int i=0;i<rows;++i)
{
MYSQL_ROW line=mysql_fetch_row(res);
for(int j=0;j<cols;++j)
{
cout<<line[j]<<"\t";
}
cout<<endl;
}
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
自此列名称我们就拿到了。
如果你将来想获取什么属性就直接去获取。
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
// cout<<"connect success"<<endl;
// 设置编码格式
mysql_set_character_set(my, "utf8");
string sql = "select * from user";
int n = mysql_query(my, sql.c_str());
if (n == 0)
{
cout << sql << " success" << endl;
}
else
{
cout << sql << " error" << endl;
return 3;
}
MYSQL_RES *res = mysql_store_result(my);
if (res == nullptr)
{
cerr << "mysql_store_result error" << endl;
return 4;
}
//下面都是和结果集有关的, MYSQL_RES
my_ulonglong rows = mysql_num_rows(res);
my_ulonglong cols = mysql_num_fields(res);
cout << "行: " << rows << endl;
cout << "列: " << cols << endl;
//属性
MYSQL_FIELD* fields=mysql_fetch_fields(res);
for(int i=0;i<cols;++i)
{
cout<<fields[i].name<<"\t";
}
cout<<endl;
//内容
for(int i=0;i<rows;++i)
{
MYSQL_ROW line=mysql_fetch_row(res);
for(int j=0;j<cols;++j)
{
cout<<line[j]<<"\t";
}
cout<<endl;
}
cout<<fields[0].db<<" "<<fields[0].table<<endl;
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
最终我们就把mysql的查询结果获取到,然后转储到结果集,然后从结果集中提取行列,还有其他属性和内容。现在我们对增删查改就有一个完整的认识了。
但是还有一个细节mysql_store_result会把查询结果由MYSQL转储到MYSQL_RES里,MYSQL_RES一定要为我们malloc一大堆空间,所以我们一定要记的 free(result),不然是肯定会造成内存泄漏的。我们不能直接用C的free或者C++的delete,而用的是mysql库提供的这个函数,把结果集中空间释放掉。
int main()
{
// 1. 初始化mysql,构建mysql对象,得到mysql访问句柄
MYSQL *my = mysql_init(nullptr);
if (my == nullptr)
{
cerr << "init MySQL error" << endl;
return 1;
}
// 2.连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "connect MySQL error" << endl;
return 2;
}
// cout<<"connect success"<<endl;
// 设置编码格式
mysql_set_character_set(my, "utf8");
string sql = "select * from user";
int n = mysql_query(my, sql.c_str());
if (n == 0)
{
cout << sql << " success" << endl;
}
else
{
cout << sql << " error" << endl;
return 3;
}
MYSQL_RES *res = mysql_store_result(my);
if (res == nullptr)
{
cerr << "mysql_store_result error" << endl;
return 4;
}
//下面都是和结果集有关的, MYSQL_RES
my_ulonglong rows = mysql_num_rows(res);
my_ulonglong cols = mysql_num_fields(res);
cout << "行: " << rows << endl;
cout << "列: " << cols << endl;
//属性
MYSQL_FIELD* fields=mysql_fetch_fields(res);
for(int i=0;i<cols;++i)
{
cout<<fields[i].name<<"\t";
}
cout<<endl;
//内容
for(int i=0;i<rows;++i)
{
MYSQL_ROW line=mysql_fetch_row(res);
for(int j=0;j<cols;++j)
{
cout<<line[j]<<"\t";
}
cout<<endl;
}
cout<<fields[0].db<<" "<<fields[0].table<<endl;
//释放结果集
mysql_free_result(res);
// 3.用完之后close,释放资源
mysql_close(my);
return 0;
}
可以看到对于读取的处理其实是挺麻烦的。
我们常用的接口就这些,对于一般数据库读取没有任何问题。
最后,mysql C api还支持事务等常用操作,大家下来自行了解:
my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
my_bool STDCALL mysql_commit(MYSQL * mysql);
my_bool STDCALL mysql_rollback(MYSQL * mysql)
目前为止所有连接数据库的操作,我们已经有第二种方案。以前就是mysql命令行式的、刚刚就是mysql的C/C++的连接方案,我们还可以通过图形化界面连接数据库。
4.图形化界面访问数据库
这里推荐几个好用的图形化界面。
Nacicat是一个桌面版的mysql数据库管理和开发工具,以图形化界面显示数据库的操作。虽然这个工具非常好用,但遗憾的是,这个软件是收费的。可以自己在网上找找它的破解版。
SQLyog 也是一个易于使用的、快速而简洁的图形化管理MYSQL数据库的工具,它能够在任何地点有效地管理你的数据库。但同样的,虽然它用起来很舒服,但它也是收费的
MYSQL Workbench是mysql官方提供的数据库管理和开发工具,支持图形化界面。虽然它在使用上并没有上面的Navicat和SQLyog好用,但优点就是它是免费的。这里我们重点就用它。
4.1下载MYSQL Workbench
我们可以直接去mysql官网上去找到它,然后下载。
往下翻找到这个然后进去
找到MySQL Workbench然后进去
选择对应的操作系统,直接下载就行了
下好之后运行就是这个样子
4.2MYSQL Workbench远程连接数据库
MYSQL Workbench也是一个客户端,说白了它和我们自己写的C/C++客户端和曾经mysql对应的命令行客户端是没有任何差别的,它也要进行网络请求,也要进行登录。所以mysql一定要允许这个用户进行远程登录。
因此我们在创建一个允许远程登录的账号。
创建一个允许用户从任意地点登录。这个任意登录这件事情,虽然我们这样写了,一般在公司内允许那一台机器访问数据库可以把这个IP地址确定下来不用填%,今天我们用的云服务器,我们内网经过NAT路由器转发IP地址会发生改变。本地IP配上去没有任何意义,所以这里只能用%。
create user 'workbench'@'%' identified by 'xxx';
然后在给新用户赋权。把conn数据库所有表的权限都给这个用户。
grant all on conn.* to 'workbench'@'%';
然后才能远程连接,点击这里+,配置一下相关信息
我们主要填的就是IP地址,端口号,和用户。还可以给这个连接起个名字。
配置好,然后点击下面的,代表测试一下连接,它会要求输入对应的密码,直接把这个用户的密码输入就行了。
如果成功连接在点击ok就是下面这样,如果连接不上可能是你端口号没有放出来。自己放一下。如果想登录的话,点击一下这个出来的东西,然后就登录进去了。
进去之后我们可以看到对应表结构、视图,存储过程和函数这些。我们现在只用关系表结构就可以了。表结构里面对应的列、索引、外键、触发器这些。
列里面可以看到对应列的属性,如id列的主机和自增长
下面我们可以在框里写一些sql指令了。写完之后选中或者光标放在你写的sql这里然后点击这个闪电执行它。然后下面就可以看到对应的信息。
虽然光标放在后面点击闪电也可以执行,但是它是从上到下开始执行的,如果只需要执行某一个sql指令,还是要选择它然后在去执行。
接下来我们可以查一下这个表,可以看到这里担心数据太多,默认只把0-1000行筛选出来。
然后可以直接在这里对数据进行插入,然后再点击Apply执行一下
Apply会把刚刚在图形化界面这里做的动作变成sql的语句,然后在点Apply执行。
然后在点Finsh,这个插入就算真正执行完成了
然后我们查一下,确实看到是插入了
接下里我们想把张三这条记录删除一下,可以直接在图形化界面删除,然后还是和上面一样的流程Apply。
然后这看到确实可以删除。
更新也是一样在图形化界面改,然后Apply执行一下。我们把李四年龄改成90岁。
看到确实也是改了
换言之,我们就不用自己写sql了,拿到表结构对它进行增删查改,直接在里面手改就行了。这就是图形化界面的好处。
如果想把自己写的一大堆sql指令保存一下,可以ctrl+s保存。形成一个.sql文件。