精华内容
下载资源
问答
  • C++封装MySQL操作函数

    万次阅读 2021-04-24 18:59:03
    C++简单封装MySQL操作函数

    1、在Linux上安装MySQL

    • 具体如何安装MySQL大家可以参考这个大佬的文章,写的超级详细 CentOS 7 MySQL安装教程,这里就不赘述了
    • 环境就用我上一篇文章搭建的那个环境就行,可以点击这里去到上一篇环境搭建教程 bifang框架运行环境搭建入门指南
    • 这里给出一份已经下载好的MySQL8.0.18的安装包 点击进入下载页面
    • 需要注意的是需要把下图的出了test之外的安装包都给安装了,上面安装教程里面没有全部安装,没全部安装的话是没有MySQL的c库的,这样就没办法在C++中使用 MySQL 了
      在这里插入图片描述
    • 安装完成之后进入,输入 show DATABASES; 查看当前已有的数据库,如图所示,证明安装完成
      在这里插入图片描述
    • 然后使用数据库软件去连接Linux的MySQL,我使用的是Navicat Premium 15,据说这个比较流行,网上有很多破解使用教程,大家可以自行百度下载使用,首先设置数据库连接信息,然后连接数据库
      在这里插入图片描述
      在这里插入图片描述
      连上之后出现如下界面就是成功了在这里插入图片描述
    • 我们在MySQL下面新建一张表,名字叫做student,用于后续测试程序用,如下图所示
      在这里插入图片描述
      time记得设置为让他自动更新就行在这里插入图片描述

    2、MySQL常用API

    要想封装出一个易用的MySQL库,就需要先知道官方究竟开放了哪些接口给我们使用,大家可以自行去网上下载MySQL对应版本源码,由于我们其实不需要知道函数实现细节(主要那玩意比较难看懂。。。),所以直接去/usr/include/mysql文件夹里面将头文件全部拉到本地就可以获取到官方提供的c接口了(MySQL安装之后头文件的目录,不同系统不同MySQL发行版本安装的位置可能会不一样,这个得自己去找一下),大部分有用的信息都在mysql.h文件里面,大家可以把这个文件看一遍,不用去看对应的源码,就看一遍大概知道有什么可用的功能就行,接下来会列举我们用到的几个重要的结构体和API
    在这里插入图片描述

    2.1、结构体

    • MYSQL_RES是存放执行结果集的,当我们执行完查询的函数之后就会返回这个结构体给我们,我们就可以利用它来将查询结果取出来,大家可以结合源码去看看里面各个变量的作用,我们是不会直接操作这个结构体,这里知道它很重要就行
    typedef struct MYSQL_RES {
      uint64_t row_count;
      MYSQL_FIELD *fields;
      struct MYSQL_DATA *data;
      MYSQL_ROWS *data_cursor;
      unsigned long *lengths; /* column lengths of current row */
      MYSQL *handle;          /* for unbuffered reads */
      const struct MYSQL_METHODS *methods;
      MYSQL_ROW row;         /* If unbuffered read */
      MYSQL_ROW current_row; /* buffer to current row */
      struct MEM_ROOT *field_alloc;
      unsigned int field_count, current_field;
      bool eof; /* Used by mysql_fetch_row */
      /* mysql_stmt_close() had to cancel this result */
      bool unbuffered_fetch_cancelled;
      enum enum_resultset_metadata metadata;
      void *extension;
    } MYSQL_RES;
    
    • MYSQL_FIELD是用于存放结果集各个字段的信息(字段名、类型、最大长度等等),这个结构体在开发中应该算是使用频率很低的,因为我们要查询表数据肯定是事先知道表中有哪些字段了,甚至在很多情况下使用select语句都会直接指定要取出的参数而不是将整个表的字段全部取出。在后续代码中这个也只是用来做结果展示用而已(可以较为方便地列出查询结果的各个字段名)
    typedef struct MYSQL_FIELD {
      char *name;               /* Name of column */
      char *org_name;           /* Original column name, if an alias */
      char *table;              /* Table of column if column was a field */
      char *org_table;          /* Org table name, if table was an alias */
      char *db;                 /* Database for table */
      char *catalog;            /* Catalog for table */
      char *def;                /* Default value (set by mysql_list_fields) */
      unsigned long length;     /* Width of column (create length) */
      unsigned long max_length; /* Max width for selected set */
      unsigned int name_length;
      unsigned int org_name_length;
      unsigned int table_length;
      unsigned int org_table_length;
      unsigned int db_length;
      unsigned int catalog_length;
      unsigned int def_length;
      unsigned int flags;         /* Div flags */
      unsigned int decimals;      /* Number of decimals in field */
      unsigned int charsetnr;     /* Character set */
      enum enum_field_types type; /* Type of field. See mysql_com.h for types */
      void *extension;
    } MYSQL_FIELD;
    
    • MYSQL_BIND这个结构体非常重要,它是用于MySQL预处理中的一个重要的组成部分,在stmt过程中,输入的数据由该结构体提供,而最终输出的结果数据也是从这个结构体出来的,不过两者不是同一个内存空间的,我们在使用中需要定义两个MYSQL_BIND列表,一个作参数输入,另一个作结果输出。这个结构体建议大家要浏览一遍,官方注释很详细,里面大部分字段都很重要,最终编程中大部分都会用到
    typedef struct MYSQL_BIND {
      unsigned long *length; /* output length pointer */
      bool *is_null;         /* Pointer to null indicator */
      void *buffer;          /* buffer to get/put data */
      /* set this if you want to track data truncations happened during fetch */
      bool *error;
      unsigned char *row_ptr; /* for the current data position */
      void (*store_param_func)(NET *net, struct MYSQL_BIND *param);
      void (*fetch_result)(struct MYSQL_BIND *, MYSQL_FIELD *, unsigned char **row);
      void (*skip_result)(struct MYSQL_BIND *, MYSQL_FIELD *, unsigned char **row);
      /* output buffer length, must be set when fetching str/binary */
      unsigned long buffer_length;
      unsigned long offset;              /* offset position for char/binary fetch */
      unsigned long length_value;        /* Used if length is 0 */
      unsigned int param_number;         /* For null count and error messages */
      unsigned int pack_length;          /* Internal length for packed data */
      enum enum_field_types buffer_type; /* buffer type */
      bool error_value;                  /* used if error is 0 */
      bool is_unsigned;                  /* set if integer type is unsigned */
      bool long_data_used;               /* If used with mysql_send_long_data */
      bool is_null_value;                /* Used if is_null is 0 */
      void *extension;
    } MYSQL_BIND;
    
    • MYSQL_STMT和MYSQL这两个结构体就不需要过多了解了,有兴趣可以看一看,相当于一个控制器的作用,调用初始化函数之后就能得到他们,在使用各种API时经常得把他们作为参数传递进去
    typedef struct MYSQL_STMT {
      struct MEM_ROOT *mem_root; /* root allocations */
      LIST list;                 /* list to keep track of all stmts */
      MYSQL *mysql;              /* connection handle */
      MYSQL_BIND *params;        /* input parameters */
      MYSQL_BIND *bind;          /* output parameters */
      MYSQL_FIELD *fields;       /* result set metadata */
      MYSQL_DATA result;         /* cached result set */
      MYSQL_ROWS *data_cursor;   /* current row in cached result */
      /*
        mysql_stmt_fetch() calls this function to fetch one row (it's different
        for buffered, unbuffered and cursor fetch).
      */
      int (*read_row_func)(struct MYSQL_STMT *stmt, unsigned char **row);
      /* copy of mysql->affected_rows after statement execution */
      uint64_t affected_rows;
      uint64_t insert_id;          /* copy of mysql->insert_id */
      unsigned long stmt_id;       /* Id for prepared statement */
      unsigned long flags;         /* i.e. type of cursor to open */
      unsigned long prefetch_rows; /* number of rows per one COM_FETCH */
      /*
        Copied from mysql->server_status after execute/fetch to know
        server-side cursor status for this statement.
      */
      unsigned int server_status;
      unsigned int last_errno;            /* error code */
      unsigned int param_count;           /* input parameter count */
      unsigned int field_count;           /* number of columns in result set */
      enum enum_mysql_stmt_state state;   /* statement state */
      char last_error[MYSQL_ERRMSG_SIZE]; /* error message */
      char sqlstate[SQLSTATE_LENGTH + 1];
      /* Types of input parameters should be sent to server */
      bool send_types_to_server;
      bool bind_param_done;           /* input buffers were supplied */
      unsigned char bind_result_done; /* output buffers were supplied */
      /* mysql_stmt_close() had to cancel this result */
      bool unbuffered_fetch_cancelled;
      /*
        Is set to true if we need to calculate field->max_length for
        metadata fields when doing mysql_stmt_store_result.
      */
      bool update_max_length;
      struct MYSQL_STMT_EXT *extension;
    } MYSQL_STMT;
    
    typedef struct MYSQL {
      NET net;                     /* Communication parameters */
      unsigned char *connector_fd; /* ConnectorFd for SSL */
      char *host, *user, *passwd, *unix_socket, *server_version, *host_info;
      char *info, *db;
      struct CHARSET_INFO *charset;
      MYSQL_FIELD *fields;
      struct MEM_ROOT *field_alloc;
      uint64_t affected_rows;
      uint64_t insert_id;      /* id if insert on table with NEXTNR */
      uint64_t extra_info;     /* Not used */
      unsigned long thread_id; /* Id for connection in server */
      unsigned long packet_length;
      unsigned int port;
      unsigned long client_flag, server_capabilities;
      unsigned int protocol_version;
      unsigned int field_count;
      unsigned int server_status;
      unsigned int server_language;
      unsigned int warning_count;
      struct st_mysql_options options;
      enum mysql_status status;
      enum enum_resultset_metadata resultset_metadata;
      bool free_me;   /* If free in mysql_close */
      bool reconnect; /* set to 1 if automatic reconnect */
    
      /* session-wide random string */
      char scramble[SCRAMBLE_LENGTH + 1];
    
      LIST *stmts; /* list of all statements */
      const struct MYSQL_METHODS *methods;
      void *thd;
      /*
        Points to boolean flag in MYSQL_RES  or MYSQL_STMT. We set this flag
        from mysql_stmt_close if close had to cancel result set of this object.
      */
      bool *unbuffered_fetch_owner;
      void *extension;
    } MYSQL;
    

    2.2、API

    // 获取结果集里面的字段数目
    unsigned int STDCALL mysql_num_fields(MYSQL_RES *res);
    // 获取结果集的MYSQL_FIELD
    MYSQL_FIELD *STDCALL mysql_fetch_fields(MYSQL_RES *res);
    // 获取结果集当前指向的行数据(MYSQL_ROW = char**)
    MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result);
    // 获取结果集指向的行数据每个字段的长度(数据指针和数据长度结合才能得出查询的真实数据)
    unsigned long *STDCALL mysql_fetch_lengths(MYSQL_RES *result);
    // 释放结果集的内存
    void STDCALL mysql_free_result(MYSQL_RES *result);
    // 释放stmt结果集的内存
    bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt);
    // 关闭stmt并释放对应的内存
    bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt);
    // 获取stmt错误码
    unsigned int STDCALL mysql_stmt_errno(MYSQL_STMT *stmt);
    // 获取stmt错误码对应的信息
    const char *STDCALL mysql_stmt_error(MYSQL_STMT *stmt);
    // 返回insert或update语句为AUTO_INCREMENT列生产的值, 在包含AUTO_INCREMENT字段的表上执行了预处理语句后使用
    uint64_t STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt);
    // 获取MySQL错误码
    unsigned int STDCALL mysql_errno(MYSQL *mysql);
    // 获取MySQL错误码对应的信息
    const char *STDCALL mysql_error(MYSQL *mysql);
    // 返回insert或update语句为AUTO_INCREMENT列生产的值,
    uint64_t STDCALL mysql_insert_id(MYSQL *mysql);
    // 返回前一次MySQL操作所影响的记录行数
    uint64_t STDCALL mysql_affected_rows(MYSQL *mysql);
    // MySQL线程相关
    bool STDCALL mysql_thread_init(void);
    void STDCALL mysql_thread_end(void);
    // 返回预处理结果集(仅包含元数据,不包含执行结果数据,经常配合mysql_num_fields和mysql_fetch_fields一起使用)
    MYSQL_RES *STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt);
    // 将stmt结果数据的机构体绑定上去(最终结果通过第二个参数返回)
    bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bnd);
    // 执行stmt命令
    int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt);
    // 获取stmt执行结果(结果返回到mysql_stmt_bind_result绑定的那个结构体里了)
    int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt);
    // 将stmt的结果集指向下一行(结果返回到mysql_stmt_bind_result绑定的那个结构体里了)
    int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt);
    // 返回stmt结果集的总行数
    uint64_t STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt);
    // 初始化stmt操作
    MYSQL_STMT *STDCALL mysql_stmt_init(MYSQL *mysql);
    // 解析stmt预处理指令
    int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
                                   unsigned long length);
    // 获取stmt所需参数个数
    unsigned long STDCALL mysql_stmt_param_count(MYSQL_STMT *stmt);
    // 将要执行的参数绑定到stmt上
    bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bnd);
    // 初始化MySQL
    MYSQL *STDCALL mysql_init(MYSQL *mysql);
    // 设置MySQL属性,具体可以设置的类型以对应版本为主,不同版本之间是会由些许差异的
    int STDCALL mysql_options(MYSQL *mysql, enum mysql_option option,
                              const void *arg);
    // 连接MySQL
    MYSQL *STDCALL 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并释放对应的内存
    void STDCALL mysql_close(MYSQL *sock);
    // ping数据库
    int STDCALL mysql_ping(MYSQL *mysql);
    // 切换数据库
    int STDCALL mysql_select_db(MYSQL *mysql, const char *db);
    // 数据库查询,执行查询操作,两个函数都可以,建议用下面那个,因为可以不用c类型的字符串作参数
    int STDCALL mysql_query(MYSQL *mysql, const char *q);
    int STDCALL mysql_real_query(MYSQL *mysql, const char *q, unsigned long length);
    // 获取查询结果
    MYSQL_RES *STDCALL mysql_store_result(MYSQL *mysql);
    /*
      mysql_server_init/end need to be called when using libmysqld or
      libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so
      you don't need to call it explicitely; but you need to call
      mysql_server_end() to free memory). The names are a bit misleading
      (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general
      names which suit well whether you're using libmysqld or libmysqlclient. We
      intend to promote these aliases over the mysql_server* ones.
    */
    // 这里贴上官方的原版注释,因为我看不懂,看不懂的就是好解释
    int STDCALL mysql_server_init(int argc, char **argv, char **groups);
    #define mysql_library_init mysql_server_init
    

    3、MySQL封装细节

    3.1、修改掉源码中的部分错误内容

    这里先给出一份已经完成的代码,点击这里下载 mysql c++封装.zip,解压之后进去编译一下发现有错误(如果没有则忽略这段直接看下面的内容),如下图所示,可以看出mysql/udf_registration_types.h有个错误,直接把错误的行给屏蔽即可编译通过
    在这里插入图片描述
    屏蔽这里在这里插入图片描述

    3.2、封装六个常用的类

    /**
     * brief: MySQL查询结果集类
     */
    class MySQLRes/**
     * brief: MySQL预处理查询结果集类(由于需要事先分配储存结果的内存, 所以需要在建表时需要确认数据长度(int, float之类的数据不需要), 不然无法使用该功能)
     */
    class MySQLStmtRes/**
     * brief: MySQL预处理类
     */
    class MySQLStmt;
    
    /**
     * brief: MySQL类
     */
    class MySQL;
    
    /**
     * brief: MySQL事务类
     */
    class MySQLTransaction;
    
    /**
     * brief: MySQL管理类
     */
    class MySQLManager;
    

    3.2.1、MySQLRes

    该类是用来存放MySQL查询结果的,在执行完查询之后应该返回这个类,类成员有以下几个:

    // 结果集列名
    std::vector<std::string> m_fields;
    // 结果集数据
    std::unordered_map<std::string, std::string*> m_datas;
    // MYSQL结果集智能指针
    std::shared_ptr<MYSQL_RES> m_res;
    

    执行结果集偏移的方法为

    /**
     * brief: 将结果集移向下一行
     * return: true  - 成功
     *         false - 失败
     */
    bool next()
    {
        if (m_fields.empty())
        {
            std::cout << "the query results have no fields!" << std::endl;
            return false;
        }
        // 数据集当前指向的行数据(MYSQL_ROW = char**)
        MYSQL_ROW cur = mysql_fetch_row(m_res.get());
        if (!cur)
            return false;
        // 当前行每一列的数据长度
        unsigned long* curLength = mysql_fetch_lengths(m_res.get());
        int len = mysql_num_fields(m_res.get());
        for (int i = 0; i < len; i++)
        {
            if (m_datas[m_fields[i]])
            {
                delete m_datas[m_fields[i]];
                m_datas[m_fields[i]] = nullptr;
            }
            if (cur[i])
                m_datas[m_fields[i]] = new std::string(cur[i], curLength[i]);
        }
        return true;
    }
    

    获取字段值的方法为

    #define XX() \
        auto it = m_datas.find(name); \
        if (it == m_datas.end()) \
            throw "field(" + name + ") is not exist!"
    
        bool isNull(const std::string& name) const
        {
            XX();
            return !it->second;
        }
        int getInt(const std::string& name) const
        {
            XX();
            return atol((*it->second).c_str());
        }
        int64_t getInt64(const std::string& name) const
        {
            XX();
            return atoll((*it->second).c_str());
        }
        float getFloat(const std::string& name) const
        {
            XX();
            return atof((*it->second).c_str());
        }
        double getDouble(const std::string& name) const
        {
            XX();
            return atof((*it->second).c_str());
        }
        std::string getString(const std::string& name) const
        {
            XX();
            return *it->second;
        }
    #undef XX
    

    3.2.2、MySQLStmtRes

    该类是用来存放stmt查询结果的,在执行完stmt的查询之后应该返回这个类
    类成员有以下几个

    // MySQL预处理类智能指针
    std::shared_ptr<MySQLStmt> m_stmt;
    // 绑定参数(相当于壳)
    std::vector<MYSQL_BIND> m_binds;
    // 结果集列名
    std::vector<std::string> m_fields;
    // 结果集数据
    std::unordered_map<std::string, Data> m_datas;
    

    其中Data为自定义的结构体,用于分配存储结果集的内存, 实现统一的写入和读取

    struct Data
    {
        ~Data()
        {
            if (buffer)
                delete[] buffer;
        }
    
        void alloc(size_t size)
        {
            if (buffer)
                delete[] buffer;
    
            buffer = new char[size];
            buffer_length = size;
        }
    
        uint64_t length = 0;
        bool is_null = false;
        bool error = false;
        char* buffer = nullptr;
        uint64_t buffer_length = 0;
        enum_field_types buffer_type = MYSQL_TYPE_NULL;
    };
    

    执行结果集偏移的方法如下所示,可以看到由于stmt的结果是从绑定的内存输出的,所以这里无须做任何处理,只要调用mysql_stmt_fetch结果数据就会流向m_datas中

    bool MySQLStmtRes::next()
    {
        return !mysql_stmt_fetch(m_stmt->get());
    }
    

    获取字段值的方法为:

    #define XX() \
        auto it = m_datas.find(name); \
        if (it == m_datas.end()) \
            throw "field(" + name + ") is not exist!"
    
        bool isNull(const std::string& name) const
        {
            XX();
            return it->second.is_null;
        }
        int8_t getInt8(const std::string& name) const
        {
            XX();
            if (it->second.buffer_type == MYSQL_TYPE_TINY)
                return *(int8_t*)it->second.buffer;
            std::string str = getString(name);
            return atol(str.c_str());
        }
        int16_t getInt16(const std::string& name) const
        {
            XX();
            if (it->second.buffer_type == MYSQL_TYPE_SHORT)
                return *(int16_t*)it->second.buffer;
            std::string str = getString(name);
            return atol(str.c_str());
        }
        int32_t getInt32(const std::string& name) const
        {
            XX();
            if (it->second.buffer_type == MYSQL_TYPE_LONG)
                return *(int32_t*)it->second.buffer;
            std::string str = getString(name);
            return atol(str.c_str());
        }
        int64_t getInt64(const std::string& name) const
        {
            XX();
            if (it->second.buffer_type == MYSQL_TYPE_LONGLONG)
                return *(int64_t*)it->second.buffer;
            std::string str = getString(name);
            return atoll(str.c_str());
        }
        float getFloat(const std::string& name) const
        {
            XX();
            if (it->second.buffer_type == MYSQL_TYPE_FLOAT)
                return *(float*)it->second.buffer;
            std::string str = getString(name);
            return atof(str.c_str());
        }
        double getDouble(const std::string& name) const
        {
            XX();
            if (it->second.buffer_type == MYSQL_TYPE_DOUBLE)
                return *(double*)it->second.buffer;
            std::string str = getString(name);
            return atof(str.c_str());
        }
        std::string getString(const std::string& name, bool is_convert = false) const
        {
            XX();
            switch (it->second.buffer_type)
            {
                case MYSQL_TYPE_TINY:
                    return std::to_string(*(int8_t*)it->second.buffer);
                case MYSQL_TYPE_SHORT:
                    return std::to_string(*(int16_t*)it->second.buffer);
                case MYSQL_TYPE_LONG:
                    return std::to_string(*(int32_t*)it->second.buffer);
                case MYSQL_TYPE_LONGLONG:
                    return std::to_string(*(int64_t*)it->second.buffer);
                case MYSQL_TYPE_FLOAT:
                    return std::to_string(*(float*)it->second.buffer);
                case MYSQL_TYPE_DOUBLE:
                    return std::to_string(*(double*)it->second.buffer);
                case MYSQL_TYPE_TIMESTAMP:
                case MYSQL_TYPE_DATETIME:
                case MYSQL_TYPE_DATE:
                case MYSQL_TYPE_TIME:
                {
                    time_t t = mysql_time_to_time_t(*(MYSQL_TIME*)it->second.buffer);
                    if (is_convert)
                        return time_to_string(t);
                    else
                        return std::to_string(t);
                }
                default:
                    return std::string(it->second.buffer, it->second.length);
            }
        }
        time_t getTime(const std::string& name) const
        {
            XX();
            if (it->second.buffer_type == MYSQL_TYPE_TIMESTAMP ||
                it->second.buffer_type == MYSQL_TYPE_DATETIME  ||
                it->second.buffer_type == MYSQL_TYPE_DATE      ||
                it->second.buffer_type == MYSQL_TYPE_TIME)
                return mysql_time_to_time_t(*(MYSQL_TIME*)it->second.buffer);
             return 0;
        }
    #undef XX
    

    3.2.3、MySQLStmt

    该类是用来管理stmt操作的,需要提供的功能由解析stmt命令、绑定参数、执行命令
    其中绑定参数要绑定两次,一次是绑定stmt传入参数的,即?的值,如下所示,在设计上,重载了一组绑定的方法,然后利用c++可变参模板来实现一个支持绑定多个参数的方法,如下所示

    // 适应各种类型参数的绑定方法
    #define BIND_XX(type, symbol, ptr, size) \
        m_binds[idx].buffer_type = type; \
        m_binds[idx].is_unsigned = symbol; \
        if (m_binds[idx].buffer == nullptr) \
        { \
            m_binds[idx].buffer = malloc(size); \
            m_binds[idx].buffer_length = size; \
        } \
        else if (m_binds[idx].buffer_length != size) \
        { \
            free(m_binds[idx].buffer); \
            m_binds[idx].buffer = malloc(size); \
            m_binds[idx].buffer_length = size; \
        } \
        memcpy(m_binds[idx].buffer, ptr, size);
    
        void bind(int idx, const MySQLNull& value)
        {
            m_binds[idx].buffer_type = MYSQL_TYPE_NULL;
            if (m_binds[idx].buffer != nullptr)
            {
                free(m_binds[idx].buffer);
                m_binds[idx].buffer = nullptr;
            }
        }
        void bind(int idx, const int8_t& value)
        {
            BIND_XX(MYSQL_TYPE_TINY, false, &value, sizeof(value));
        }
        void bind(int idx, const uint8_t& value)
        {
            BIND_XX(MYSQL_TYPE_TINY, true, &value, sizeof(value));
        }
        void bind(int idx, const int16_t& value)
        {
            BIND_XX(MYSQL_TYPE_SHORT, false, &value, sizeof(value));
        }
        void bind(int idx, const uint16_t& value)
        {
            BIND_XX(MYSQL_TYPE_SHORT, true, &value, sizeof(value));
        }
        void bind(int idx, const int32_t& value)
        {
            BIND_XX(MYSQL_TYPE_LONG, false, &value, sizeof(value));
        }
        void bind(int idx, const uint32_t& value)
        {
            BIND_XX(MYSQL_TYPE_LONG, true, &value, sizeof(value));
        }
        void bind(int idx, const int64_t& value)
        {
            BIND_XX(MYSQL_TYPE_LONGLONG, false, &value, sizeof(value));
        }
        void bind(int idx, const uint64_t& value)
        {
            BIND_XX(MYSQL_TYPE_LONGLONG, true, &value, sizeof(value));
        }
        void bind(int idx, const float& value)
        {
            BIND_XX(MYSQL_TYPE_FLOAT, false, &value, sizeof(value));
        }
        void bind(int idx, const double& value)
        {
            BIND_XX(MYSQL_TYPE_DOUBLE, false, &value, sizeof(value));
        }
        void bind(int idx, const std::string& value)
        {
            BIND_XX(MYSQL_TYPE_STRING, false, value.c_str(), value.size());
        }
        //
        void bind(int idx, const void* value, uint32_t size)
        {
            BIND_XX(MYSQL_TYPE_BLOB, false, value, size);
        }
        void bind(int idx, const void* value, uint64_t size)
        {
            BIND_XX(MYSQL_TYPE_BLOB, false, value, size);
        }
        //
        void bind(int idx, const MySQLTime& value)
        {
            MYSQL_TIME mt = time_t_to_mysql_time(value.ts);
            BIND_XX(MYSQL_TYPE_TIMESTAMP, false, &mt, sizeof(MYSQL_TIME));
        }
    // 利用可变参模板设计的一个能同时绑定多个参数的方法
    	template<typename... Args>
        void multibind(Args... args)
        {
            binder(0, args...);
        }
    
        void binder(size_t N)
        {
            //std::cout << "multibind end" << std::endl;
        }
        template<typename... Args>
        void binder(size_t N, const void* value, uint64_t size, Args... args)
        {
            if (N >= m_binds.size())
                return;
            bind(N, value, size);
            binder(N + 1, args...);
        }
        template<typename T, typename... Args>
        void binder(size_t N, T value, Args... args)
        {
            if (N >= m_binds.size())
                return;
            bind(N, value);
            binder(N + 1, args...);
        }
    

    另一个绑定输出结果参数的函数如下所示,可以看到确实将m_datas的内存空间交给了stmt去使用

    MySQLStmtRes::ptr MySQLStmtRes::create(std::shared_ptr<MySQLStmt> stmt)
    {
        if (stmt->getErrno())
        {
            std::cout << "stmt error, errno=" << stmt->getErrno()
                << ", errstr=" << stmt->getErrstr() << std::endl;
            return nullptr;
        }
        MySQLStmtRes::ptr ret(new MySQLStmtRes(stmt));
    
        MYSQL_RES* res = mysql_stmt_result_metadata(stmt->get());
        if (!res)
        {
            std::cout << "mysql_stmt_result_metadata error, errno="
                << stmt->getErrno() << ", errstr=" << stmt->getErrstr() << std::endl;
            return nullptr;
        }
        int len = mysql_num_fields(res);
        MYSQL_FIELD* fields = mysql_fetch_fields(res);
        ret->m_binds.resize(len);
        memset(&ret->m_binds[0], 0, sizeof(ret->m_binds[0]) * len);
    
    #define XX(m, t) \
        case m: \
            ret->m_datas[name].alloc(sizeof(t)); \
            break
        for (int i = 0; i < len; i++)
        {
            std::string name = std::string(fields[i].name, fields[i].name_length);
            ret->m_fields.push_back(name);
    
            switch (fields[i].type)
            {
                XX(MYSQL_TYPE_TINY, int8_t);
                XX(MYSQL_TYPE_SHORT, int16_t);
                XX(MYSQL_TYPE_LONG, int32_t);
                XX(MYSQL_TYPE_LONGLONG, int64_t);
                XX(MYSQL_TYPE_FLOAT, float);
                XX(MYSQL_TYPE_DOUBLE, double);
                XX(MYSQL_TYPE_TIMESTAMP, MYSQL_TIME);
                XX(MYSQL_TYPE_DATETIME, MYSQL_TIME);
                XX(MYSQL_TYPE_DATE, MYSQL_TIME);
                XX(MYSQL_TYPE_TIME, MYSQL_TIME);
                default:
                    ret->m_datas[name].alloc(fields[i].length);
                    break;
            }
    
            ret->m_datas[name].buffer_type = fields[i].type;
    
            ret->m_binds[i].length = &ret->m_datas[name].length;
            ret->m_binds[i].is_null = &ret->m_datas[name].is_null;
            ret->m_binds[i].buffer = ret->m_datas[name].buffer;
            ret->m_binds[i].error = &ret->m_datas[name].error;
            ret->m_binds[i].buffer_length = ret->m_datas[name].buffer_length;
            ret->m_binds[i].buffer_type = ret->m_datas[name].buffer_type;
        }
    #undef XX
    
        if (mysql_stmt_bind_result(stmt->get(), &ret->m_binds[0]))
        {
            std::cout << "mysql_stmt_bind_result error, errno="
                << stmt->getErrno() << ", errstr=" << stmt->getErrstr() << std::endl;
            return nullptr;
        }
    
        if (mysql_stmt_execute(stmt->get()))
        {
            std::cout << "mysql_stmt_execute error, errno="
                << stmt->getErrno() << ", errstr=" << stmt->getErrstr() << std::endl;
            return nullptr;
        }
    
        if (mysql_stmt_store_result(stmt->get()))
        {
            std::cout << "mysql_stmt_store_result error, errno="
                << stmt->getErrno() << ", errstr=" << stmt->getErrstr() << std::endl;
            return nullptr;
        }
    
        return ret;
    }
    

    3.2.4、MySQL

    该类是最重要的一个,实现起来较为简单,需要对外提供的功能有:初始化数据库、连接数据库、ping、切换数据库、执行命令获取结果、创建预处理还有创建事务等等,这里就不全部展开讲了,贴上connect方法和query方法,其余的功能大家可以下载源码去看一下

    // 需要注意的是如果代码hook了read和write函数的话,MySQL的MYSQL_OPT_RECONNECT选项不能开启(血与泪的教训。。。)
    bool MySQL::connect()
    {
        static thread_local MySQLThreadInit s_thread_init;
    
        if (m_mysql && !m_hasError)
            return true;
    
        MYSQL* mysql = ::mysql_init(nullptr);
        if (mysql == nullptr)
        {
            std::cout << "mysql_init error" << std::endl;
            m_hasError = true;
            return false;
        }
    
        int auto_reconnect = 0;
        mysql_options(mysql, MYSQL_OPT_RECONNECT, &auto_reconnect);
        mysql_options(mysql, MYSQL_SET_CHARSET_NAME, "utf8mb4"); // 用utf8mb4是为了兼容unicode
        if (mysql_real_connect(mysql, m_host.c_str(), m_user.c_str(),
                m_passwd.c_str(), m_dbname.c_str(), m_port, NULL, 0) == nullptr)
        {
            std::cout << "mysql_real_connect(" << m_host
                      << ", " << m_port << ", " << m_dbname
                      << ") error: " << mysql_error(mysql) << std::endl;
            mysql_close(mysql);
            m_hasError = true;
            return false;
        }
    
        m_hasError = false;
        m_mysql.reset(mysql, mysql_close);
        return true;
    }
    
    // 这里可以看到query返回的结果为MySQLRes的智能指针,和我们一开始说的设计是一致的,最终从MySQLRes中获取结果
    MySQLRes::ptr MySQL::query(const char* format, ...)
    {
        if (!m_mysql)
        {
            std::cout << "m_mysql is NULL" << std::endl;
            m_hasError = true;
            return nullptr;
        }
    
        std::string cmd;
        {
            va_list ap;
            va_start(ap, format);
            char* buf = nullptr;
            int len = vasprintf(&buf, format, ap);
            if (len != -1)
            {
                cmd.append(buf, len);
                free(buf);
            }
            va_end(ap);
        }
    
        if (::mysql_real_query(m_mysql.get(), &cmd[0], cmd.size()))
        {
            std::cout << "mysql_real_query(" << cmd << ") error:" << getErrstr() << std::endl;
            m_hasError = true;
            return nullptr;
        }
        MYSQL_RES* res = mysql_store_result(m_mysql.get());
        if (res == nullptr)
        {
            std::cout << "mysql_store_result(" << cmd << ") error:" << getErrstr() << std::endl;
            m_hasError = true;
            return nullptr;
        }
    
        m_hasError = false;
        MySQLRes::ptr ret(new MySQLRes(res));
        return ret;
    }
    

    3.2.5、MySQLTransaction

    该类为事务类,事务的接口实现较为简单,这里只实现了最简单的形式,即开始事务、提交事务、回滚事务。由于实现起来太简单了这里就不贴出具体代码了

    3.2.6、MySQLManager

    该类是一个管理类,用于统一管理所有 MySQL 连接,这样可以很方便地结合配置文件来使用MySQL,而且也可以在每次分配连接时都检查是否需要重新连接数据库,防止服务器因为超时把连接断开,并且提供了回收机制,当MySQL池的数据小于我们设置的容量时,每一个被释放的连接都可以重新回到数据池里面循环使用,是借用智能指针来实现这个功能的,大家有兴趣可以看一看具体实现的做法

    /**
     * brief: MySQL管理类
     */
    class MySQLManager
    {
    public:
        typedef Mutex MutexType;
    
        struct MySqlConf
        {
            std::string host;
            int port;
            std::string user;
            std::string passwd;
            std::string dbname;
            uint32_t poolSize = 10;
        };
    
        MySQLManager();
    
        ~MySQLManager();
    
        void add(const std::string& name, const std::string& host, int port,
            const std::string& user, const std::string& passwd,
            const std::string& dbname, uint32_t poolSize = 10);
    
        MySQL::ptr get(const std::string& name);
    
        bool execute(const std::string& name, const char* format, ...);
        bool execute(const std::string& name, const std::string& cmd);
    
        MySQLRes::ptr query(const std::string& name, const char* format, ...);
        MySQLRes::ptr query(const std::string& name, const std::string& cmd);
    
        MySQLStmt::ptr openPrepare(const std::string& name, const std::string& cmd);
    
        MySQLTransaction::ptr openTransaction(const std::string& name, bool auto_commit);
    
        void checkConnection(int sec = 30);
    
    private:
        void freeMySQL(const std::string& name, MySQL* m);
    
    private:
        MutexType m_mutex;
        std::unordered_map<std::string, std::list<MySQL*> > m_connections;
        std::unordered_map<std::string, MySqlConf> m_sqlDefines;
    };
    

    4、总结并附上本文源代码

    MySQL官方给出的接口整体来说还是比较清晰的,注释也很不错,不过还是建议要先有一定数据库的基础再去看这些。
    最后附上一份源代码,大家可以下载下去调试使用看看,有什么错误的地方也欢迎大家指出
    mysql c++封装.zip

    展开全文
  • Python3操作mysql操作

    千次阅读 2018-10-08 20:08:00
    #下载mysql.connector 由于我的Anaconda装了2和3,所以我首先在cm中使用activate命令切换到python3环境下,...下面基于mysql.connector写了一个简单的mysql操作类: # -*- coding: utf-8 -*- import mysql.connector...

    #下载mysql.connector
    由于我的Anaconda装了2和3,所以我首先在cm中使用activate命令切换到python3环境下,然后使用

    pip  install mysql.connector
    

    在这里插入图片描述

    #Mysql操作类
    下面基于mysql.connector写了一个简单的mysql操作类:

    # -*- coding: utf-8 -*-
    import mysql.connector
    
    
    class MyDB():
         def __init__(self , host="127.0.0.1", username="root", password="112233", port=3306, database="test"):
            '''类例化,处理一些连接操作'''
            self.host = host
            self.username = username
            self.password = password
            self.database = database
            self.port = port
            self.cur = None
            self.con = None
            # connect to mysql
            try:
                self.con = mysql.connector.connect(host = self.host, user = self.username, password = self.password, port = self.port, database = self.database)
                self.cur = self.con.cursor()
            except :
                raise "DataBase connect error,please check the db config."
    
         def close(self):
            '''结束查询和关闭连接'''
            self.con.close()
    
         def create_table(self,sql_str):
            '''创建数据表'''
            try:
                self.cur.execute(sql_str)
            except Exception as e:
                print(e)
         def query_formatrs(self,sql_str):
             '''查询数据,返回一个列表,里面的每一行是一个字典,带字段名
                 cursor 为连接光标
                 sql_str为查询语句
             '''
             try:
                 self.cur.execute(sql_str)
                 rows = self.cur.fetchall()
                 r = []
                 for x in rows:
                     r.append(dict(zip(self.cur.column_names,x)))
    
                 return r
             except:
                 return False
    
         def query(self,sql_str):
            '''查询数据并返回
                 cursor 为连接光标
                 sql_str为查询语句
            '''
            try:
    
                self.cur.execute(sql_str)
                rows = self.cur.fetchall()
                return rows
            except:
                return False
    
         def execute_update_insert(self,sql):
            '''
            插入或更新记录 成功返回最后的id
            '''
            self.cur.execute(sql)
            self.con.commit()
            return self.cur.lastrowid
    
    if __name__ == "__main__":
        mydb = MyDB()
        #创建表
        mydb.create_table('create table user (id varchar(20) primary key, name varchar(20))')
        #插入数据
        mydb.execute_update_insert("insert into user (id, name) values  ('1', 'Michael')")
        # 查询数据表
        mydb_new = MyDB()
        results = mydb.query("SELECT * FROM detailinfo")
        print(results)
        for row in results:
            productname = row[0]
            supportname = row[1]
            productinterface = row[2]
            print("productname=%s,supportname=%s,productinterface=%s" % \
                   (productname, supportname, productinterface))
        list = mydb.query_formatrs("SELECT * FROM detailinfo")
        for i in list:
            print ("记录号:%s   值:%s" % (list.index(i) + 1, i))
        #关闭数据库
        mydb.close()
    

    执行该程序可以看到数据库"user"成功创建,并且插入了一条数据。再者,在控制台中成功打印了数据库detailinfo中的数据。如下面的一些图所示:
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    展开全文
  • cmd进入mysql操作 win+R,输入cmd,打开cmd窗口,进入到 mysql bin目录的路径下 第一步:启动mysql服务,可以通过“net start myql”命令实现; 第二步:先使用DOS命令进入mysql的安装目录下的bin目录中; 第三...

    cmd进入mysql操作

    win+R,输入cmd,打开cmd窗口,进入到 mysql bin目录的路径下

    第一步:启动mysql服务,可以通过“net start myql”命令实现;

    第二步:先使用DOS命令进入mysql的安装目录下的bin目录中;

    第三步:在命令行输入:mysql -u 用户名 -p密码;回车;-h表示服务器名,localhost表示本地,-hlocalhost 可不输入;-u为数据库用户名,root是mysql默认用户名;-p为密码,如果设置了密码,可直接在-p后链接输入,如:-p123456,用户没有设置密码,显示Enter password时,直接回车即可。)

    显示结果:

    cmd下的mysql操作

    数据库有关操作

    1.查询时间:select now();

    2.查询当前用户:select user();

    3.查询数据库版本:select version();

    4.列出数据库:show databases;

    5.选择数据库:use databaseName;

    6.建立数据库:create database databaseName;

    7.查看新创建的数据库信息:show create database databaseName;

    8.删除数据库:drop database databaseName;

    数据表有关操作

    1.查看数据表存储引擎:show engines;

    2.列出表格:show tables;

    3.创建表:CREATE TABLE tableName(
        c_num int (11) not null  unique primary key auto_increment,
        c_name varchar (50),
        c_contact varchar (50),
        c_city varchar (50),
        c_birth datetime not null
    );

    4.查看表结构:desc tableName;

    5.显示表格列的属性:show columns from tableName;

    6.修改字段类型:alter table tableName modify fieldName newFieldType

    7.字段改名:alter table tableName change oldFieldName newFieldName newFieldType

    8.表改名:alter table oldTableName rename newTableName;

    9.复制表:create table tableName2 select * from ttableName1;

    10. 插入表中一行记录:insert into tableName values ("value1","value2","value3"......);

    11. 删除表中一行记录:delete from tableName where columnName=value; //不加where将删除全部数据

    12. 更新表中一行记录:update tableName set columnName=value where columnName=value

    13. 查询表中所有记录:select * from tableName;

    14.删除表:drop TABLE tableName;

    备注:

    MySQL会使用索引的操作符号

      <,<=,>=,>,=,between,in,不带%或者_开头的like

    mysql退出

    在dos下运行net start mysql 不能启动mysql!提示发生系统错误 5;拒绝访问!切换到管理员模式就可以启动了。所以要以管理员身份来运行cmd程序来启动mysql。

    dos命令的基本操作:

    盘符: 例如想进入D盘   d:   
    cd      进入到当前盘某个目录。
    cd \    进入当前盘根目录
    cd \windows  进入到当前盘Windows目录
    cd..    退出到上一级目录

    注:  进入含有特殊字符目录时需要加引号  例如 cd "c:\program files"

    在cmd中,不需要你全输入,你只需要按p键,然后按tab键,就可以定位,以p字母开头的文件/文件夹名。多次按tab键可切换文件/文件名

    展开全文
  • Ubuntu 打开mysql 操作日志

    千次阅读 2019-06-17 00:34:07
    LINUX中打开MYSQL操作日志 # 配置文件 sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf # 配置日志的存储位置 general_log_file = /var/log/mysql/mysql.log # 打开日志记录 general_log = 1 # 重启mysql sudo ...

    LINUX中打开MYSQL操作日志

    # 配置文件
    sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
    # 配置日志的存储位置
    general_log_file = /var/log/mysql/mysql.log
    # 打开日志记录
    general_log = 1
    # 重启mysql
    sudo service mysql restart
    # 实时查看日志记录变化
    tail -F /var/log/mysql/mysql.log
    
    展开全文
  • Linux下mysql操作命令大全

    千次阅读 2018-09-18 14:07:19
    Linux下mysql操作命令大全连接mysql数据库命令mysql数据库操作命令mysql数据表操作命令 连接mysql数据库命令 通用连接命令:mysql -uroot -p123456 mysql数据库操作命令 显示数据库:mysql&gt; show databases; ...
  • Linux中MySQL操作

    万次阅读 2019-04-19 13:09:05
    一、登录MySQL 1. 启动: MYSQL启动文件在/etc/init.d目录下,启动运行以下命令 /etc/init.d/mysql start 2. 停止: /usr/bin/mysqladmin -u root -p shutdown 3. 登录: mysql [-u username] [-h host] [-p ...
  • Mysql操作语句-插入语句

    千次阅读 2018-01-13 16:29:29
    Mysql操作语句总结 数据库插入语句(多条数据插入):
  • TarsCpp 组件之 MySQL 操作

    千次阅读 2020-11-02 20:18:23
    作者 Eaton 导语 在后台开发中,我们经常需要和数据库打交道,而在 C++ 开发中,MySQL Connector/C++ 只提供了基础...目录MySQL简介MySQL 常用 API存在的问题TC_Mysql数据库配置接口 TC_DBConf数据库操作类 TC_Mysql.
  • #首先在mysql里建好数据库名 #mysql -uroot -p密码 数据库名 < D:/备份文件名.sql
  • 查看mysql操作日志

    千次阅读 2018-12-17 16:58:28
    找到mysql的安装目录,找到my.ini文件 打开my.ini文件,找到log-bin参数,该参数对应的路径就是操作日志所在的路径 由于操作日志是二进制文件,我们直接使用记事本是无法打开的,所以需要把二进制文件转换成txt...
  • egg.js中使用egg-mysql操作mysql数据库

    万次阅读 多人点赞 2019-08-15 10:05:51
    1、在 egg 项目中安装 egg-mysql npm i egg-mysql --save 2、在 {app_root}/config/plugin.js 中启用 egg-mysql 插件: 'use strict'; /** @type Egg.EggPlugin */ module.exports = { //mysql mysql:{ enable:...
  • mysql操作datetime字段统一加上一周的时间:update test set time = DATE_ADD(time,INTERVAL 7 DAY)
  • 1.前言本文将介绍iOS中的数据库操作,主要涉及SQLite本地数据库操作,以及MySQL...2.2MySQL操作一般来说,直接操作MySQL的情景很少,但是有时候如果服务端没能及时给出接口,同时我们又需要测试客户端,那么此时可能就
  • MySql操作缓慢:copy to tmp table

    万次阅读 2016-03-17 17:15:07
    MySql操作缓慢:copy to tmp table 在给表加字段的时候,发现执行几分钟还没完成,查了下数据量,不到200w条记录,不应该出现这种情况。  后来想了想可能是别的线程锁表了。  新建窗口,执行 show processlist,...
  • MFC调用mysql操作

    千次阅读 2016-12-30 17:34:16
    要用MySQL提供的C语言API,首先要包含API的头文件目录,也就是在MFC工程属性中的“包含目录”下添加MySQL安装目录的“include”文件夹。因为API是以动态链接库的形式打包的,所以还要在MFC工程属性中的“库目录”下...
  • php中对MYSQL操作之事务控制,回滚

    千次阅读 2014-08-12 10:31:40
    php中对MYSQL操作之事务控制,回滚
  • mysql操作命令记录

    千次阅读 2016-01-04 10:27:28
    一:安装完mysql后,会缺少 /etc/my.cnf 文件,可以手动添加。 [client] port = 3306 #socket = /var/lib/mysql/mysql.sock default-character-set=utf8 [mysqld] #basedir=/db/mysql-5.6.23-linux-glibc2.5-x86
  • mysql操作json字段

    千次阅读 2019-07-22 11:52:49
    update m_imports set properties = json_replace(properties, '$.defaults.tags',json_array('153','154')) 创建表 CREATETABLEt_json(idINTPRIMARYKEY,NAMEVARCHAR(20),infoJSON); ...IN...
  • 1. mysql操作篇之一 最基本的几个命令 a. Mysql安装方法 从官网下载二进制包 http://www.mysql.com/ 如果是Linux系统下,直接yum install mysql*,mysql-server*即可 然后service mysqld start启动mysql 数据库。 ...
  • 同linux command保存在~/.bash_history一样,你用mysql连接MySQL server的所有操作也会被记录到~/.mysql_history文件中,这样就会有很大的安全风险了,如添加MySQL用户的sql也同样会被明文记录到此文件中。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 259,220
精华内容 103,688
关键字:

mysql操作

mysql 订阅