【MySQL】mysql访问
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_RESmy_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_RESmy_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_RESmy_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_RESmy_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文件。
相关文章:

【MySQL】mysql访问
mysql访问 1.引入MySQL 客户端库2.C/C 进行增删改3.查询的处理细节4.图形化界面访问数据库4.1下载MYSQL Workbench4.2MYSQL Workbench远程连接数据库 点赞👍👍收藏🌟🌟关注💖💖 你的支持是对我最大的鼓励&a…...

(1)Jupyter Notebook 下载及安装
目录 1. Jupyter Notebook是什么?2. Jupyter Notebook特征3. 组成部分3.1 网页应用3.2 文档 4. 适用场景5. 利用Google Colab安装Jupyter Notebook3.1 什么是 Colab?3.2 访问 Google Colab3.3 新建笔记本 1. Jupyter Notebook是什么? 百度百科…...

监控平台zabbix对接grafana
本次博客基于监控平台zabbix介绍与部署-CSDN博客的环境下进行的 1、安装grafana并启动 添加一台虚拟机20.0.0.30 (1)系统初始化 [rootzx3 ~]# systemctl stop firewalld [rootzx3 ~]# setenforce 0 [rootzx3 ~]#(2)安装并启动…...

14-11 2024 年的 13 个 AI 趋势
2024 年的 13 个 AI 趋势 人工智能对环境的影响和平人工智能人工智能支持的问题解决和决策针对人工智能公司的诉讼2024 年美国总统大选与人工智能威胁人工智能、网络犯罪和社会工程威胁人工智能治疗孤独与对人工智能的情感依赖人工智能影响者中国争夺人工智能霸主地位人工智能…...

计算机大方向的选择
选专业要了解自己的兴趣所在。 即想要学习什么样的专业,如果有明确的专业意向,就可以有针对性地选择那些专业实力较强的院校。 2.如果没有明确的专业意向,可以优先考虑一下院校。 确定一下自己想要选择综合性院校还是理工类院校或是像财经或者…...

使用Qt Installer Framework在centos7中打包
文章目录 步骤 1: 安装Qt和Qt Installer Framework安装Qt安装Qt Installer Framework步骤 2: 创建项目目录结构步骤 3: 编写安装脚本配置文件(config/config.xml)Package 信息meta/package.xmldata 目录步骤 4: 编写安装脚本步骤 5: 生成安装程序总结在CentOS 7中使用Qt Inst…...

您的私人办公室!-----ONLYOFFICE8.1版本的桌面编辑器测评
随时随地创建并编辑文档,还可就其进行协作 ONLYOFFICE 文档是一款强大的在线编辑器,为您使用的平台提供文本文档、电子表格、演示文稿、表单和 PDF 编辑工具。 网页地址链接: https://www.onlyoffice.com/zh/office-suite.aspxhttps://www…...

点估计和参数分布的对比
点估计(Point Estimation)和 参数分布(Parameter Distribution)是统计学中两种不同的参数估计方法。 文章目录 点估计(Point Estimation)参数分布(Parameter Distribution)对比总结 …...

桌面保存的Word文件删除怎么找回?超实用的三个方法?
在日常工作和学习中,我们经常会使用Word文档进行文字编辑和文件保存。但是,有时由于操作失误或系统故障,我们会不小心将存放在电脑桌面重要的Word文件删除了。导致无法挽回的损失,但幸运的是,有一些方法可以帮助我们找…...

【leetcode】双指针算法题
文章目录 1.算法思想2.移动零3.复写零方法一方法二 4.快乐数5.盛水最多的容器方法一(暴力求解)方法二(左右指针) 6.有效三角形的个数方法一(暴力求解)方法二(左右指针) 7.两数之和8.…...

vue-router 源码分析——8.重定向
这是对vue-router 3 版本的源码分析。 本次分析会按以下方法进行: 按官网的使用文档顺序,围绕着某一功能点进行分析。这样不仅能学习优秀的项目源码,更能加深对项目的某个功能是如何实现的理解。这个对自己的技能提升,甚至面试时…...

CAN总线协议
CAN总线协议,全程为控制器局域网(Controller Area Network)协议,是一种用于实时应用的串行通讯协议。该协议由德国某公司专门为汽车行业开发,并逐渐成为一种标准,这是国际上应用最广泛的现场总线之一。 一…...

NLP篇1
场景:假设给你一篇文章。 目标:说白了,就是数学的分类。但是如何实现分类呢。下面将逐步一 一 分析与拆解。先把目标定好了和整体框架定好了。而不是只见树木而不见森林。 情感分类(好评、差评,中性) 整体…...

【一念发动便是行】念头,就是命运
一个个恶念累积就是负能量,念头就是命运,克除恶念,防范念头,念头都有能量,学圣学须内外庄严检肃,言语有灵 多数人的问题都是出在念头上,念头,就是自己的命运; 当我们对自…...

Django + Vue 实现图片上传功能的全流程配置与详细操作指南
文章目录 前言图片上传步骤1. urls 配置2. settings 配置3. models 配置4. 安装Pillow 前言 在现代Web应用中,图片上传是一个常见且重要的功能。Django作为强大的Python Web框架,结合Vue.js这样的现代前端框架,能够高效地实现这一功能。本文将…...

【介绍下R-tree,什么是R-tree?】
🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…...

每天10个js面试题(二)
1.事件轮询? JavaScript 是单线程的,同一时间只能做一件事。所有任务都需要排队,前一个任务结束,才会执行后一个任务,为了保证任务有序的执行,事件轮询就是单线程任务调度的一种方式,单线程任务…...

深入理解【 String类】
目录 1、String类的重要性 2、常用方法 2、1 字符串构造 2、2 String对象的比较 2、3 字符串查找 2、4字符转换 数值和字符串转换: 大小写转化: 字符串转数组: 格式转化: 2、5 字符串替换 2、6字符串拆分 2、7 字符串…...

Nacos 2.x 系列【20】集群部署
文章目录 1. 前言2. 部署服务端2.1 准备工作2.2 集群节点配置2.3 鉴权配置2.4 配置数据源2.5 配置 IP2.6 配置端口2.7 启动集群 3. 部署模式3.1 直连模式3.2 地址服务器模式3.2.1 地址服务器3.2.2 配置 3.3 VIP 模式(推荐)3.3.1 Nginx3.3.1 域名 1. 前言…...

LeetCode刷题记录:(15)三角形最小路径和
知识点:倒叙的动态规划 题目传送 解法一:二维动态规划【容易理解】 class Solution {public int minimumTotal(List<List<Integer>> triangle) {int n triangle.size();if (n 1) {return triangle.get(0).get(0);}// dp[i][j]:走到第i层第…...

【大数据面试题】35 Spark 怎么做优化?
一步一个脚印,一天一道大数据面试题 博主希望能够得到大家的点赞收,藏支持!非常感谢~ 点赞,收藏是情分,不点是本分。祝你身体健康,事事顺心! Spark 如何做优化一直是面试过程中常问的问题。那么这次也仅以此篇文章总结梳理,希望对大家有帮助。 通用优化 Spark 一般遇…...

2024年保安员职业资格考试题库大数据揭秘,冲刺高分!
186.安全技术防范是一种由探测、()、快速反应相结合的安全防范体系。 A.保安 B.出警 C.延迟 D.监控 答案:C 187.安全技术防范是以()和预防犯罪为目的的一项社会公共安全业务。 A.预防灾害 B.预防损失 C.预防失…...

怎么搭建个人博客教程,附云主机选购指南
一、搭建个人博客教程 1. 规划博客内容与技术栈 确定博客主题:首先明确博客的定位和主题,这将影响后续的技术选择和内容规划。选择技术栈:根据个人偏好和技术背景,选择合适的建站技术。例如,可以使用WordPress&#…...

使用Llama3/Qwen2等开源大模型,部署团队私有化Code Copilot和使用教程
目前市面上有不少基于大模型的 Code Copilot 产品,部分产品对于个人开发者来说可免费使用,比如阿里的通义灵码、百度的文心快码等。这些免费的产品均通过 API 的方式提供服务,因此调用时均必须联网、同时需要把代码、提示词等内容作为 API 的…...

C语言_结构体初阶(还未写完)
结构体的声明 1. 什么是结构?结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量 数组:一组相同类型元素的集合 结构体:一组不一定相同类型元素的集 2. 结构的声明 struct tag //tag根据实际情况给名字…...

MyBatis-Plus:快速入门
1. 概念 MyBatis-Plus(简称 MP)是一个MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。其突出的特性如下: * **无侵入**:只做增强不做改变,引入它不会对现有…...

【高级篇】第9章 Elasticsearch 监控与故障排查
9.1 引言 在现代数据驱动的应用架构中,Elasticsearch不仅是海量数据索引和搜索的核心,其稳定性和性能直接影响到整个业务链路的健康度。因此,建立有效的监控体系和掌握故障排查技能是每一位Elasticsearch高级专家的必备能力。 9.2 监控工具:洞察与优化的利器 在Elastics…...

【前端】上传和下载zip文件,有进度条(el-progess)
文章目录 上传下载进度条 场景:要上传一个zip,调用接口,然后下载一个zip。调用接口的接口响应要显示在进度条中。 上传 上传用的是input原生控件,在页面中隐藏。accept"application/zip"限制只能上传zip。 点击button…...

2024年软件测试面试题,精选100+,附答案+文档
🍅 视频学习:文末有免费的配套视频可观看 🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 Part1 1、你的测试职业发展是什么? 测试经验越多,测试能力越高。所以我…...

在vue项目的.gitignore文件忽略不想要提交到git仓库的文件
在Vue项目中,使用.gitignore文件来忽略不需要提交到Git仓库的文件是一个常见的做法。.gitignore文件包含了一系列的规则,这些规则告诉Git哪些文件或目录应该被忽略。以下是一些Vue项目中常用的.gitignore文件示例和具体规则说明: 示例 .gitig…...