【c++】数据库连接池库:libzdb

https://github.com/mverbert/libzdb

1. 引入项目

地址:http://www.tildeslash.com/libzdb/

2、安装

./configure
make
make install
若出现 --without-<database> 错误,说明你电脑上没有安装数据库,因为libzdb在安装过程中为自动检测是否有安装好的数据库。
此外可以通过,./configure --prefix=/opt/libzdb 来指定安装路径。

3、把新共享库目录加入到共享库配置文件/etc/ld.so.conf中, 如下:

# cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
# echo "<prefix>/lib" >> /etc/ld.so.conf
# ldconfig

如果在configure命令中没有指定--prefix,则为默认值/usr/local。文中指定为 /opt/libzdb .

echo /opt/libzdb/lib >> /etc/ld.so.conf

4. Makefile

CC = gcc
CFLAGS = -Wall -g
LDFLAGS = -L/opt/oldzdb/lib -lzdb
INCLUDES = -I/opt/oldzdb/include/zdb

all: my_program

my_program: main.c
	$(CC) $(CFLAGS) $(INCLUDES) main.c -o my_program $(LDFLAGS)

clean:
	rm -f my_program

5、使用

  • 有zdb.h和zdbpp.h,若使用cpp则引入zdbpp.h
  • 使用事务,可将绑定多个参数的过程分开,从而在绑定参数值的时候可以分别判断绑定的值
//这种写法会报错,类型
conn.execute(
            "insert into e_tunnel_locInfo "
            "(`mining_loc`,`tmstamp`,`day_step`, `heading_mil_S`, `firstChnNO_S`, `firstChnNO_R`, `firstChnNO_E`)"
            "values(?,FROM_UNIXTIME(?),?,?,?,?,?)",
            hzi::tunnel_mining_loc, 
            static_cast<double>(tmstamp), 
            hzi::day_step, hzi::heading_mil, 
            (firstChnNO_S==-1?nullptr:firstChnNO_S), 
            (firstChnNO_R==-1?nullptr:firstChnNO_R), 
            (firstChnNO_E==-1?nullptr:firstChnNO_E));

//使用事务分开绑定,即可判断
//异常定义在zdbpp.h中
zdb::sql_exception

//封装连接池ConnectionPool指针
using conn_pool_t = std::shared_ptr<ConnectionPool>;
static inline conn_pool_t pConnPool;

//设定url和最大连接数,启动
config.pConnPool = std::make_shared<ConnectionPool>(config.mysqlURL);
config.pConnPool->setMaxConnections(maxConnections);
config.pConnPool->start();

//获取连接对象
Connection conn = config.pConnPool->getConnection();

//执行查询(可用?占位符)
ResultSet result = conn.executeQuery(const char*);//传字面量字符串或string::c_str()
auto rslt2 = conn.executeQuery(
                    "SELECT mining_loc,day_step  FROM `e_tunnel_locInfo`"
                    " WHERE tmstamp <= ? ORDER BY tmstamp DESC LIMIT 1",
                    (double)sampTime);

//封装
template <typename... Args>
tl::expected<ResultSet, std::string> queryDb(Connection &conn, const char *sql,
                                             Args... args) 
{
  try 
  {
    ResultSet result = conn.executeQuery(sql, args...);
    if (result.next()) 
    {
      return result;
    } else {
      return tl::make_unexpected("not found in query: " + std::string(sql));
    }
  } catch (sql_exception &e) {
    return tl::make_unexpected("sql error for " + std::string(sql) + ": " + std::string(e.what()));
  }
}
bool ret = queryDb(conn,"SELECT * FROM e_users where user_name=? and password=PASSWORD(?)",user, pswd)?true:false;

queryDb(conn, sql.c_str()) 
    .map([&](ResultSet set)
     {
         //对查询结果set进行操作
     }
    .map_error([&](auto err)
     {
         //对错误信息err进行操作
     }


//执行查询之外的增删改操作(可用?占位符)
conn.execute(const char*);
conn.execute(
            "INSERT INTO "
            " e_tunnel_RunningDataInfo (start_time, "
            "end_time, is_running, "
            "start_mining_locx, "
            "end_mining_locx, "
            "recv_count, "
            "conn_status ) "
            " values(FROM_UNIXTIME(?),FROM_UNIXTIME(?),?,?,?,?,?)",
            start_time / 1000.0, end_time / 1000.0, isRunning,
            start_mining_locx, end_mining_locx, recv_count, conn_status);

//插入NULL值(若表字段允许NULL)
直接传入nullptr/NULL即可

//事务(可用?占位符)
Connection conn = hzi::config.pConnPool->getConnection();
PreparedStatement prp = conn.prepareStatement(
"INSERT INTO "
" e_tunnel_locInfo (mining_loc,tmstamp) "
" values(?,?) ON DUPLICATE KEY UPDATE tmstamp = values(tmstamp)");
conn.beginTransaction();  //启动事务
prp.bind(1, mining_locx);  //绑定参数值
prp.bind(2, (double)tmstamp);
prp.execute();  //执行事务
conn.commit();  //提交或回滚rollback()


//只取一条记录
//需注意若结果非空,if调用1次next()已经读了一条记录
if(rslt1.next())
{
  //取一条记录
}
else
{
  //为空
}

//获取结果
while (rslt1.next()) 
{
  //获取全部记录
}

//方法的作用是检查指定列索引的值是否为 SQL 空值(NULL)。如果该列的值为 NULL,则返回 true;否则返回 false
//columnIndex从1开始
bool ResultSet::isnull(int columnIndex);

//若查询出的记录很多,可预先设定先少取一点:预取行数
ResultSet::setFetchSize(int n);//先预取n行记录,避免一次查询太多

zdbpp.h

/*
 * Copyright (C) 2016 dragon jiang<jianlinlong@gmail.com>
 * Copyright (C) 2019-2022 Tildeslash Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files(the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions :
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef _ZDBPP_H_
#define _ZDBPP_H_

#include "zdb.h"
#include <tuple>
#include <string>
#include <utility>
#include <stdexcept>

/*
 
 This interface contains all the classes and methods that are needed to
 use libzdb from C++ (C++17 or later). To use libzdb in your C++ project,
 import zdbpp.h (this interface) and use the namespace zdb:

 #include <zdbpp.h>
 using namespace zdb;
 
  
 Query Example
 -------------

 ConnectionPool pool("mysql://192.168.11.100:3306/test?user=root&password=dba");
 pool.start();
 Connection con = pool.getConnection();
 // Use C++ variadic template feature to bind parameter
 ResultSet result = con.executeQuery(
      "select id, name, hired, image from employee where id < ? order by id", 100
 );
 // Optionally set row prefetch, default is 100
 result.setFetchSize(10);
 while (result.next()) {
      int id = result.getInt("id");
      const char *name = result.getString("name");
      time_t hired = result.getTimestamp("hired");
      auto [image, size] = result.getBlob("image");
      ...
 }
      
 
 Execute statement
 -----------------

 Connection con = pool.getConnection();
 // Any execute or executeQuery statement which takes parameters are
 // automatically translated into a prepared statement. Here we also
 // demonstrate how to set a SQL null value by using nullptr
 con.execute("update employee set image = ? where id = ?", nullptr, 11);
                 
 
 Test for SQL null value
 -----------------------

 ResultSet result = con.executeQuery("select name, image from employee");
 while (result.next()) {
     if (result.isnull("image")) {
         ...
     }
 }
 
 
 Insert Data via Prepared Statement
 ----------------------------------

 Connection con = pool.getConnection();
 PreparedStatement prep = con.prepareStatement(
      "insert into employee (name, hired, image) values(?, ?, ?)"
 );
 con.beginTransaction();
 for (const auto &employee : employees) {
         // Polymorphic bind
         prep.bind(1, employee.name);
         prep.bind(2, employee.hired);
         prep.bind(3, employee.image);
         prep.execute();
 }
 con.commit();
         
 
 Exception Handling
 ------------------

 try {
     con = pool.getConnection();
     con.executeQuery("invalid query");
 } catch (sql_exception& e) {
     std::cout <<  e.what();
 }

 */

namespace zdb {
    
    class sql_exception : public std::runtime_error
    {
    public:
        sql_exception(const char* msg = "SQLException")
        : std::runtime_error(msg)
        {}
    };
    
#define except_wrapper(f) TRY { f; } ELSE {throw sql_exception(Exception_frame.message);} END_TRY
    
    struct noncopyable
    {
        noncopyable() = default;
        
        // make it noncopyable
        noncopyable(noncopyable const&) = delete;
        noncopyable& operator=(noncopyable const&) = delete;
        
        // make it not movable
        noncopyable(noncopyable&&) = delete;
        noncopyable& operator=(noncopyable&&) = delete;
    };
    
    
    class URL: private noncopyable
    {
    public:
        URL(const std::string& url)
        :URL(url.c_str())
        {}
        
        URL(const char *url) {
            t_ = URL_new(url);
        }
        
        ~URL() {
            if (t_)
                URL_free(&t_);
        }
        
        operator URL_T() {
            return t_;
        }
        
    public:
        const char *protocol() const {
            return URL_getProtocol(t_);
        }
        
        const char *user() const {
            return URL_getUser(t_);
        }
        
        const char *password() const {
            return URL_getPassword(t_);
        }
        
        const char *host() const {
            return URL_getHost(t_);
        }
        
        int port() const {
            return URL_getPort(t_);
        }
        
        const char *path() const {
            return URL_getPath(t_);
        }
        
        const char *queryString() const {
            return URL_getQueryString(t_);
        }
        
        const char **parameterNames() const {
            return URL_getParameterNames(t_);
        }
        
        const char *parameter(const char *name) const {
            return URL_getParameter(t_, name);
        }
        
        const char *tostring() const {
            return URL_toString(t_);
        }
        
    private:
        URL_T t_;
    };
    

    class ResultSet : private noncopyable
    {
    public:
        operator ResultSet_T() {
            return t_;
        }
        
        ResultSet(ResultSet&& r)
        :t_(r.t_)
        {
            r.t_ = nullptr;
        }
        
    protected:
        friend class PreparedStatement;
        friend class Connection;
        
        ResultSet(ResultSet_T t)
        :t_(t)
        {}
        
    public:
        int columnCount() {
            return ResultSet_getColumnCount(t_);
        }
        
        const char *columnName(int columnIndex) {
            except_wrapper( RETURN ResultSet_getColumnName(t_, columnIndex) );
        }
        
        long columnSize(int columnIndex) {
            except_wrapper( RETURN ResultSet_getColumnSize(t_, columnIndex) );
        }

        void setFetchSize(int prefetch_rows) {
            ResultSet_setFetchSize(t_, prefetch_rows);
        }

        int getFetchSize() {
            return ResultSet_getFetchSize(t_);
        }

        bool next() {
            except_wrapper( RETURN ResultSet_next(t_) );
        }
        
        bool isnull(int columnIndex) {
            except_wrapper( RETURN ResultSet_isnull(t_, columnIndex) );
        }
        
        const char *getString(int columnIndex) {
            except_wrapper( RETURN ResultSet_getString(t_, columnIndex) );
        }
        
        const char *getString(const char *columnName) {
            except_wrapper( RETURN ResultSet_getStringByName(t_, columnName) );
        }
        
        int getInt(int columnIndex) {
            except_wrapper( RETURN ResultSet_getInt(t_, columnIndex) );
        }
        
        int getInt(const char *columnName) {
            except_wrapper( RETURN ResultSet_getIntByName(t_, columnName) );
        }
        
        long long getLLong(int columnIndex) {
            except_wrapper( RETURN ResultSet_getLLong(t_, columnIndex) );
        }
        
        long long getLLong(const char *columnName) {
            except_wrapper( RETURN ResultSet_getLLongByName(t_, columnName) );
        }
        
        double getDouble(int columnIndex) {
            except_wrapper( RETURN ResultSet_getDouble(t_, columnIndex) );
        }
        
        double getDouble(const char *columnName) {
            except_wrapper( RETURN ResultSet_getDoubleByName(t_, columnName) );
        }
        
        template <typename T>
        std::tuple<const void*, int> getBlob(T v) {
            int size = 0;
            const void *blob = nullptr;
            if constexpr (std::is_integral<T>::value)
                except_wrapper( blob = ResultSet_getBlob(t_, v, &size) );
            else
                except_wrapper( blob = ResultSet_getBlobByName(t_, v, &size) );
            return {blob, size};
        }
        
        time_t getTimestamp(int columnIndex) {
            except_wrapper( RETURN ResultSet_getTimestamp(t_, columnIndex) );
        }
        
        time_t getTimestamp(const char *columnName) {
            except_wrapper( RETURN ResultSet_getTimestampByName(t_, columnName) );
        }
        
        struct tm getDateTime(int columnIndex) {
            except_wrapper( RETURN ResultSet_getDateTime(t_, columnIndex) );
        }
        
        struct tm getDateTime(const char *columnName) {
            except_wrapper( RETURN ResultSet_getDateTimeByName(t_, columnName) );
        }

    private:
        ResultSet_T t_;
    };
    

    class PreparedStatement : private noncopyable
    {
    public:
        operator PreparedStatement_T() {
            return t_;
        }
        
        PreparedStatement(PreparedStatement&& r)
        :t_(r.t_)
        {
            r.t_ = nullptr;
        }
        
    protected:
        friend class Connection;
        
        PreparedStatement(PreparedStatement_T t)
        :t_(t)
        {}
        
    public:
        void setString(int parameterIndex, const char *x) {
            except_wrapper( PreparedStatement_setString(t_, parameterIndex, x) );
        }
        
        void setInt(int parameterIndex, int x) {
            except_wrapper( PreparedStatement_setInt(t_, parameterIndex, x) );
        }
        
        void setLLong(int parameterIndex, long long x) {
            except_wrapper( PreparedStatement_setLLong(t_, parameterIndex, x) );
        }
        
        void setDouble(int parameterIndex, double x) {
            except_wrapper( PreparedStatement_setDouble(t_, parameterIndex, x) );
        }
        
        void setBlob(int parameterIndex, const void *x, int size) {
            except_wrapper( PreparedStatement_setBlob(t_, parameterIndex, x, size) );
        }
        
        void setTimestamp(int parameterIndex, time_t x) {
            except_wrapper( PreparedStatement_setTimestamp(t_, parameterIndex, x) );
        }
        
        void execute() {
            except_wrapper( PreparedStatement_execute(t_) );
        }
        
        ResultSet executeQuery() {
            except_wrapper(
                           ResultSet_T r = PreparedStatement_executeQuery(t_);
                           RETURN ResultSet(r);
                           );
        }
        
        long long rowsChanged() {
            return PreparedStatement_rowsChanged(t_);
        }
        
        int getParameterCount() {
            return PreparedStatement_getParameterCount(t_);
        }
        
    public:
        void bind(int parameterIndex, const char *x) {
            this->setString(parameterIndex, x);
        }
        
        void bind(int parameterIndex, const std::string& x) {
            this->setString(parameterIndex, x.c_str());
        }
        
        void bind(int parameterIndex, int x) {
            this->setInt(parameterIndex, x);
        }
        
        void bind(int parameterIndex, long long x) {
            this->setLLong(parameterIndex, x);
        }
        
        void bind(int parameterIndex, double x) {
            this->setDouble(parameterIndex, x);
        }
        
        void bind(int parameterIndex, time_t x) {
            this->setTimestamp(parameterIndex, x);
        }
        
        //blob
        void bind(int parameterIndex, std::tuple<const void *, int> x) {
            auto [blob, size] = x;
            this->setBlob(parameterIndex, blob, size);
        }
        
    private:
        PreparedStatement_T t_;
    };
    

    class Connection : private noncopyable
    {
    public:
        operator Connection_T() {
            return t_;
        }
        
        ~Connection() {
            if (t_) {
                close();
            }
        }
        
    protected:  // for ConnectionPool
        friend class ConnectionPool;
        
        Connection(Connection_T C)
        :t_(C)
        {}
        
        void setClosed() {
            t_ = nullptr;
        }
        
    public:
        void setQueryTimeout(int ms) {
            Connection_setQueryTimeout(t_, ms);
        }
        
        int getQueryTimeout() {
            return Connection_getQueryTimeout(t_);
        }
        
        void setMaxRows(int max) {
            Connection_setMaxRows(t_, max);
        }
        
        int getMaxRows() {
            return Connection_getMaxRows(t_);
        }
        
        void setFetchSize(int rows) {
            Connection_setFetchSize(t_, rows);
        }
        
        int getFetchSize() {
            return Connection_getFetchSize(t_);
        }
        
        //not supported
        //URL_T Connection_getURL(T C);
        
        bool ping() {
            return Connection_ping(t_);
        }
        
        void clear() {
            Connection_clear(t_);
        }
        
        //after close(), t_ is set to NULL. so this Connection object can not be used again!
        void close() {
            if (t_) {
                Connection_close(t_);
                setClosed();
            }
        }
        
        void beginTransaction() {
            except_wrapper( Connection_beginTransaction(t_) );
        }
        
        void commit() {
            except_wrapper( Connection_commit(t_) );
        }
        
        void rollback() {
            except_wrapper( Connection_rollback(t_) );
        }
        
        long long lastRowId() {
            return Connection_lastRowId(t_);
        }
        
        long long rowsChanged() {
            return Connection_rowsChanged(t_);
        }
        
        void execute(const char *sql) {
            except_wrapper( Connection_execute(t_, "%s", sql) );
        }
        
        template <typename ...Args>
        void execute(const char *sql, Args ... args) {
            PreparedStatement p(this->prepareStatement(sql, args...));
            p.execute();
        }
        
        ResultSet executeQuery(const char *sql) {
            except_wrapper(
                           ResultSet_T r = Connection_executeQuery(t_, "%s", sql);
                           RETURN ResultSet(r);
                           );
        }
        
        template <typename ...Args>
        ResultSet executeQuery(const char *sql, Args ... args) {
            PreparedStatement p(this->prepareStatement(sql, args...));
            return p.executeQuery();
        }
        
        PreparedStatement prepareStatement(const char *sql) {
            except_wrapper(
                           PreparedStatement_T p = Connection_prepareStatement(t_, "%s", sql);
                           RETURN PreparedStatement(p);
                           );
        }
        
        template <typename ...Args>
        PreparedStatement prepareStatement(const char *sql, Args ... args) {
            except_wrapper(
                           PreparedStatement p(this->prepareStatement(sql));
                           int i = 1;
                           (p.bind(i++, args), ...);
                           RETURN p;
                           );
        }
        
        const char *getLastError() {
            return Connection_getLastError(t_);
        }
        
        static bool isSupported(const char *url) {
            return Connection_isSupported(url);
        }
        
    private:
        Connection_T t_;
    };
    
    
    class ConnectionPool : private noncopyable
    {
    public:
        ConnectionPool(const std::string& url)
        :ConnectionPool(url.c_str())
        {}
        
        ConnectionPool(const char* url)
        :url_(url)
        {
            if (!url_)
                throw sql_exception("Invalid URL");
            t_ = ConnectionPool_new(url_);
        }
        
        ~ConnectionPool() {
            ConnectionPool_free(&t_);
        }
        
        operator ConnectionPool_T() {
            return t_;
        }
        
    public:
        const URL& getURL() {
            return url_;
        }
        
        void setInitialConnections(int connections) {
            ConnectionPool_setInitialConnections(t_, connections);
        }
        
        int getInitialConnections() {
            return ConnectionPool_getInitialConnections(t_);
        }
        
        void setMaxConnections(int maxConnections) {
            ConnectionPool_setMaxConnections(t_, maxConnections);
        }
        
        int getMaxConnections() {
            return ConnectionPool_getMaxConnections(t_);
        }
        
        void setConnectionTimeout(int connectionTimeout) {
            ConnectionPool_setConnectionTimeout(t_, connectionTimeout);
        }
        
        int getConnectionTimeout() {
            return ConnectionPool_getConnectionTimeout(t_);
        }
        
        void setAbortHandler(void(*abortHandler)(const char *error)) {
            ConnectionPool_setAbortHandler(t_, abortHandler);
        }
        
        void setReaper(int sweepInterval) {
            ConnectionPool_setReaper(t_, sweepInterval);
        }
        
        int size() {
            return ConnectionPool_size(t_);
        }
        
        int active() {
            return ConnectionPool_active(t_);
        }
        
        void start() {
            except_wrapper( ConnectionPool_start(t_) );
        }
        
        void stop() {
            if (this->active() > 0) {
                throw sql_exception("Trying to stop the pool with active Connections. Please close all active Connections first");
            }
            ConnectionPool_stop(t_);
        }
        
        Connection getConnection() {
            except_wrapper(
                           Connection_T c = ConnectionPool_getConnectionOrException(t_);
                           RETURN Connection(c);
                           );
        }
        
        void returnConnection(Connection& con) {
            con.close();
        }
        
        int reapConnections() {
            return ConnectionPool_reapConnections(t_);
        }
        
        static const char *version(void) {
            return ConnectionPool_version();
        }
        
    private:
        URL url_;
        ConnectionPool_T t_;
    };
    
    
} // namespace

#endif
posted @ 2025-03-26 15:33  仰望星河Leon  阅读(70)  评论(0)    收藏  举报