C++ web框架drogon 使用对象关系映射ORM(Object Relational Mapping)模式

在drogon框架之中,加入了ORM功能,使用在C++代码中嵌入很多的SQL语句,不仅让代码凌乱难看,更是需要很好的数据库语句编写功底,而使用ORM,则能够讲操作数据库转化为操作对象,在ORM中,通常一张表对应一个类,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。 

ORM提供了对数据库的映射,不用直接编写SQL代码,只需操作对象就能对数据库操作数据。

让软件开发人员专注于业务逻辑的处理,提高了开发效率。

Model

使用Drogon的ORM支持,首先要创建Model类,Drogon的命令行程序drogon_ctl提供了生成Model类的功能,它从用户指定的数据库读取表信息,根据这些信息自动生成多个Model类的源文件。用户使用Model时include对应的头文件即可。

显而易见,每一个Model类,对应一个特定的数据库表,每个Model类的实例,对应表的一行记录。

tables配置项是它特有的,是一个字符串数组,每个字符串表示要转化成Model的表名,如果该项为空,则所有的表都会生成对应的Model类。

用drogon_ctl create project命令创建的工程目录下已经预先创建了models目录和对应的model.json文件,用户可以编辑配置文件后用drogon_ctl命令创建Model类

model类的创建命令如下:

drogon_ctl create model 

最后一个参数是model存放的路径,该路径内必须有一个配置文件model.json,用以配置drogon_ctl到数据库的连接参数,一般是直接使用models目录下得model.json。它是JSON格式的文件,支持注释,例子如下:

{
    //rdbms: server type, postgresql,mysql or sqlite3
    "rdbms": "mysql",
    //filename: sqlite3 db file name
    //"filename":"",
    //host: server address,localhost by default;
    "host": "127.0.0.1",
    //port: server port, 5432 by default;
    "port": 3306,
    //dbname: Database name;
    "dbname": "REST",
    //schema: valid for postgreSQL, "public" by default;
    "schema": "public",
    //user: User name
    "user": "root",
    //password or passwd: Password
    "password": "********",
    //client_encoding: The character set used by drogon_ctl. it is empty string by default which 
    //means use the default character set.
    //"client_encoding": "",
    //table: An array of tables to be modelized. if the array is empty, all revealed tables are modelized.
    "tables": ["api_image_test"],
    //convert: the value can be changed by a function call before it is stored into database or
    //after it is read from database
    "convert": {
      "enabled": false,
      "items":[{
          "table": "user",
          "column": "password",
          "method": {
            //after_db_read: name of the method which is called after reading from database, signature: void([const] std::shared_ptr [&])
            "after_db_read": "decrypt_password",
            //before_db_write: name of the method which is called before writing to database, signature: void([const] std::shared_ptr [&])
            "before_db_write": "encrypt_password"
          },
          "includes": [
            "\"file_local_search_path.h\"","<file_in_global_search_path.h>"
          ]
      }]
    },
    "relationships": {
        "enabled": false,
        "items": [{
                "type": "has one",
                "original_table_name": "products",
                "original_table_alias": "product",
                "original_key": "id",
                "target_table_name": "skus",
                "target_table_alias": "SKU",
                "target_key": "product_id",
                "enable_reverse": true
            },
            {
                "type": "has many",
                "original_table_name": "products",
                "original_table_alias": "product",
                "original_key": "id",
                "target_table_name": "reviews",
                "target_table_alias": "",
                "target_key": "product_id",
                "enable_reverse": true
            },
            {
                "type": "many to many",
                "original_table_name": "products",
                "original_table_alias": "",
                "original_key": "id",
                "pivot_table": {
                    "table_name": "carts_products",
                    "original_key": "product_id",
                    "target_key": "cart_id"
                },
                "target_table_name": "carts",
                "target_table_alias": "",
                "target_key": "id",
                "enable_reverse": true
            }
        ]
    },
    "restful_api_controllers": {
        "enabled": false,
        // resource_uri: The URI to access the resource, the default value 
        // is '/*' in which the asterisk represents the table name.
        // If this option is set to a empty string, the URI is composed of the namespaces and the class name.
        "resource_uri": "/*",
        // class_name: "Restful*Ctrl" by default, the asterisk represents the table name.
        // This option can contain namespaces.
        "class_name": "Restful*Ctrl",
        // filters: an array of filter names.
        "filters": [],
        // db_client: the database client used by the controller. this option must be consistent with
        // the configuration of the application.
        "db_client": {
            //name: Name of the client,'default' by default
            "name": "default",
            //is_fast: 
            "is_fast": false
        },
        // directory: The directory where the controller source files are stored.
        "directory": "controllers",
        // generate_base_only: false by default. Set to true to avoid overwriting custom subclasses.
        "generate_base_only": false
    }
}

参数中需要填写数据库相应的配置数据,包括数据库地址,密码,数据库名,表名,这里的数据库和表必须是已经在数据库中已经存在的,这里与Django的ORM不用,不会自动创建数据库,而是将已经创建好的数据库在modles目录下新建一个类。

使用如下指令:

drongo_ctl create model modles

执行后将在models文件夹下产生.cc和.h文件,为表对应的类创建文件

/**
 *
 *  ApiImageTest.cc
 *  DO NOT EDIT. This file is generated by drogon_ctl
 *
 */

#include "ApiImageTest.h"
#include <drogon/utils/Utilities.h>
#include <string>

using namespace drogon;
using namespace drogon_model::REST;

const std::string ApiImageTest::Cols::_id = "id";
const std::string ApiImageTest::Cols::_device_name = "device_name";
const std::string ApiImageTest::Cols::_img_url = "img_url";
const std::string ApiImageTest::primaryKeyName = "id";
const bool ApiImageTest::hasPrimaryKey = true;
const std::string ApiImageTest::tableName = "api_image_test";

const std::vector<typename ApiImageTest::MetaData> ApiImageTest::metaData_={
{"id","int32_t","int",4,1,1,1},
{"device_name","std::string","varchar(64)",64,0,0,1},
{"img_url","std::string","varchar(100)",100,0,0,0}
};
const std::string &ApiImageTest::getColumnName(size_t index) noexcept(false)
{
    assert(index < metaData_.size());
    return metaData_[index].colName_;
}
ApiImageTest::ApiImageTest(const Row &r, const ssize_t indexOffset) noexcept
{
    if(indexOffset < 0)
    {
        if(!r["id"].isNull())
        {
            id_=std::make_shared<int32_t>(r["id"].as<int32_t>());
        }
        if(!r["device_name"].isNull())
        {
            deviceName_=std::make_shared<std::string>(r["device_name"].as<std::string>());
        }
        if(!r["img_url"].isNull())
        {
            imgUrl_=std::make_shared<std::string>(r["img_url"].as<std::string>());
        }
    }
    else
    {
        size_t offset = (size_t)indexOffset;
        if(offset + 3 > r.size())
        {
            LOG_FATAL << "Invalid SQL result for this model";
            return;
        }
        size_t index;
        index = offset + 0;
        if(!r[index].isNull())
        {
            id_=std::make_shared<int32_t>(r[index].as<int32_t>());
        }
        index = offset + 1;
        if(!r[index].isNull())
        {
            deviceName_=std::make_shared<std::string>(r[index].as<std::string>());
        }
        index = offset + 2;
        if(!r[index].isNull())
        {
            imgUrl_=std::make_shared<std::string>(r[index].as<std::string>());
        }
    }

}

ApiImageTest::ApiImageTest(const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false)
{
    if(pMasqueradingVector.size() != 3)
    {
        LOG_ERROR << "Bad masquerading vector";
        return;
    }
    if(!pMasqueradingVector[0].empty() && pJson.isMember(pMasqueradingVector[0]))
    {
        dirtyFlag_[0] = true;
        if(!pJson[pMasqueradingVector[0]].isNull())
        {
            id_=std::make_shared<int32_t>((int32_t)pJson[pMasqueradingVector[0]].asInt64());
        }
    }
    if(!pMasqueradingVector[1].empty() && pJson.isMember(pMasqueradingVector[1]))
    {
        dirtyFlag_[1] = true;
        if(!pJson[pMasqueradingVector[1]].isNull())
        {
            deviceName_=std::make_shared<std::string>(pJson[pMasqueradingVector[1]].asString());

        }
    }
    if(!pMasqueradingVector[2].empty() && pJson.isMember(pMasqueradingVector[2]))
    {
        dirtyFlag_[2] = true;
        if(!pJson[pMasqueradingVector[2]].isNull())
        {
            imgUrl_=std::make_shared<std::string>(pJson[pMasqueradingVector[2]].asString());

        }
    }
}

ApiImageTest::ApiImageTest(const Json::Value &pJson) noexcept(false)
{
    if(pJson.isMember("id"))
    {
        dirtyFlag_[0]=true;
        if(!pJson["id"].isNull())
        {
            id_=std::make_shared<int32_t>((int32_t)pJson["id"].asInt64());
        }
    }
    if(pJson.isMember("device_name"))
    {
        dirtyFlag_[1]=true;
        if(!pJson["device_name"].isNull())
        {
            deviceName_=std::make_shared<std::string>(pJson["device_name"].asString());
        }
    }
    if(pJson.isMember("img_url"))
    {
        dirtyFlag_[2]=true;
        if(!pJson["img_url"].isNull())
        {
            imgUrl_=std::make_shared<std::string>(pJson["img_url"].asString());
        }
    }
}

void ApiImageTest::updateByMasqueradedJson(const Json::Value &pJson,
                                            const std::vector<std::string> &pMasqueradingVector) noexcept(false)
{
    if(pMasqueradingVector.size() != 3)
    {
        LOG_ERROR << "Bad masquerading vector";
        return;
    }
    if(!pMasqueradingVector[0].empty() && pJson.isMember(pMasqueradingVector[0]))
    {
        if(!pJson[pMasqueradingVector[0]].isNull())
        {
            id_=std::make_shared<int32_t>((int32_t)pJson[pMasqueradingVector[0]].asInt64());
        }
    }
    if(!pMasqueradingVector[1].empty() && pJson.isMember(pMasqueradingVector[1]))
    {
        dirtyFlag_[1] = true;
        if(!pJson[pMasqueradingVector[1]].isNull())
        {
            deviceName_=std::make_shared<std::string>(pJson[pMasqueradingVector[1]].asString());
        }
    }
    if(!pMasqueradingVector[2].empty() && pJson.isMember(pMasqueradingVector[2]))
    {
        dirtyFlag_[2] = true;
        if(!pJson[pMasqueradingVector[2]].isNull())
        {
            imgUrl_=std::make_shared<std::string>(pJson[pMasqueradingVector[2]].asString());
        }
    }
}
                                                                    
void ApiImageTest::updateByJson(const Json::Value &pJson) noexcept(false)
{
    if(pJson.isMember("id"))
    {
        if(!pJson["id"].isNull())
        {
            id_=std::make_shared<int32_t>((int32_t)pJson["id"].asInt64());
        }
    }
    if(pJson.isMember("device_name"))
    {
        dirtyFlag_[1] = true;
        if(!pJson["device_name"].isNull())
        {
            deviceName_=std::make_shared<std::string>(pJson["device_name"].asString());
        }
    }
    if(pJson.isMember("img_url"))
    {
        dirtyFlag_[2] = true;
        if(!pJson["img_url"].isNull())
        {
            imgUrl_=std::make_shared<std::string>(pJson["img_url"].asString());
        }
    }
}

const int32_t &ApiImageTest::getValueOfId() const noexcept
{
    const static int32_t defaultValue = int32_t();
    if(id_)
        return *id_;
    return defaultValue;
}
const std::shared_ptr<int32_t> &ApiImageTest::getId() const noexcept
{
    return id_;
}
void ApiImageTest::setId(const int32_t &pId) noexcept
{
    id_ = std::make_shared<int32_t>(pId);
    dirtyFlag_[0] = true;
}



const typename ApiImageTest::PrimaryKeyType & ApiImageTest::getPrimaryKey() const
{
    assert(id_);
    return *id_;
}

const std::string &ApiImageTest::getValueOfDeviceName() const noexcept
{
    const static std::string defaultValue = std::string();
    if(deviceName_)
        return *deviceName_;
    return defaultValue;
}
const std::shared_ptr<std::string> &ApiImageTest::getDeviceName() const noexcept
{
    return deviceName_;
}
void ApiImageTest::setDeviceName(const std::string &pDeviceName) noexcept
{
    deviceName_ = std::make_shared<std::string>(pDeviceName);
    dirtyFlag_[1] = true;
}
void ApiImageTest::setDeviceName(std::string &&pDeviceName) noexcept
{
    deviceName_ = std::make_shared<std::string>(std::move(pDeviceName));
    dirtyFlag_[1] = true;
}




const std::string &ApiImageTest::getValueOfImgUrl() const noexcept
{
    const static std::string defaultValue = std::string();
    if(imgUrl_)
        return *imgUrl_;
    return defaultValue;
}
const std::shared_ptr<std::string> &ApiImageTest::getImgUrl() const noexcept
{
    return imgUrl_;
}
void ApiImageTest::setImgUrl(const std::string &pImgUrl) noexcept
{
    imgUrl_ = std::make_shared<std::string>(pImgUrl);
    dirtyFlag_[2] = true;
}
void ApiImageTest::setImgUrl(std::string &&pImgUrl) noexcept
{
    imgUrl_ = std::make_shared<std::string>(std::move(pImgUrl));
    dirtyFlag_[2] = true;
}


void ApiImageTest::setImgUrlToNull() noexcept
{
    imgUrl_.reset();
    dirtyFlag_[2] = true;
}


void ApiImageTest::updateId(const uint64_t id)
{
    id_ = std::make_shared<int32_t>(static_cast<int32_t>(id));
}

const std::vector<std::string> &ApiImageTest::insertColumns() noexcept
{
    static const std::vector<std::string> inCols={
        "device_name",
        "img_url"
    };
    return inCols;
}

void ApiImageTest::outputArgs(drogon::orm::internal::SqlBinder &binder) const
{
    if(dirtyFlag_[1])
    {
        if(getDeviceName())
        {
            binder << getValueOfDeviceName();
        }
        else
        {
            binder << nullptr;
        }
    }
    if(dirtyFlag_[2])
    {
        if(getImgUrl())
        {
            binder << getValueOfImgUrl();
        }
        else
        {
            binder << nullptr;
        }
    }
}

const std::vector<std::string> ApiImageTest::updateColumns() const
{
    std::vector<std::string> ret;
    if(dirtyFlag_[1])
    {
        ret.push_back(getColumnName(1));
    }
    if(dirtyFlag_[2])
    {
        ret.push_back(getColumnName(2));
    }
    return ret;
}

void ApiImageTest::updateArgs(drogon::orm::internal::SqlBinder &binder) const
{
    if(dirtyFlag_[1])
    {
        if(getDeviceName())
        {
            binder << getValueOfDeviceName();
        }
        else
        {
            binder << nullptr;
        }
    }
    if(dirtyFlag_[2])
    {
        if(getImgUrl())
        {
            binder << getValueOfImgUrl();
        }
        else
        {
            binder << nullptr;
        }
    }
}
Json::Value ApiImageTest::toJson() const
{
    Json::Value ret;
    if(getId())
    {
        ret["id"]=getValueOfId();
    }
    else
    {
        ret["id"]=Json::Value();
    }
    if(getDeviceName())
    {
        ret["device_name"]=getValueOfDeviceName();
    }
    else
    {
        ret["device_name"]=Json::Value();
    }
    if(getImgUrl())
    {
        ret["img_url"]=getValueOfImgUrl();
    }
    else
    {
        ret["img_url"]=Json::Value();
    }
    return ret;
}

Json::Value ApiImageTest::toMasqueradedJson(
    const std::vector<std::string> &pMasqueradingVector) const
{
    Json::Value ret;
    if(pMasqueradingVector.size() == 3)
    {
        if(!pMasqueradingVector[0].empty())
        {
            if(getId())
            {
                ret[pMasqueradingVector[0]]=getValueOfId();
            }
            else
            {
                ret[pMasqueradingVector[0]]=Json::Value();
            }
        }
        if(!pMasqueradingVector[1].empty())
        {
            if(getDeviceName())
            {
                ret[pMasqueradingVector[1]]=getValueOfDeviceName();
            }
            else
            {
                ret[pMasqueradingVector[1]]=Json::Value();
            }
        }
        if(!pMasqueradingVector[2].empty())
        {
            if(getImgUrl())
            {
                ret[pMasqueradingVector[2]]=getValueOfImgUrl();
            }
            else
            {
                ret[pMasqueradingVector[2]]=Json::Value();
            }
        }
        return ret;
    }
    LOG_ERROR << "Masquerade failed";
    if(getId())
    {
        ret["id"]=getValueOfId();
    }
    else
    {
        ret["id"]=Json::Value();
    }
    if(getDeviceName())
    {
        ret["device_name"]=getValueOfDeviceName();
    }
    else
    {
        ret["device_name"]=Json::Value();
    }
    if(getImgUrl())
    {
        ret["img_url"]=getValueOfImgUrl();
    }
    else
    {
        ret["img_url"]=Json::Value();
    }
    return ret;
}

bool ApiImageTest::validateJsonForCreation(const Json::Value &pJson, std::string &err)
{
    if(pJson.isMember("id"))
    {
        if(!validJsonOfField(0, "id", pJson["id"], err, true))
            return false;
    }
    if(pJson.isMember("device_name"))
    {
        if(!validJsonOfField(1, "device_name", pJson["device_name"], err, true))
            return false;
    }
    else
    {
        err="The device_name column cannot be null";
        return false;
    }
    if(pJson.isMember("img_url"))
    {
        if(!validJsonOfField(2, "img_url", pJson["img_url"], err, true))
            return false;
    }
    return true;
}
bool ApiImageTest::validateMasqueradedJsonForCreation(const Json::Value &pJson,
                                                      const std::vector<std::string> &pMasqueradingVector,
                                                      std::string &err)
{
    if(pMasqueradingVector.size() != 3)
    {
        err = "Bad masquerading vector";
        return false;
    }
    try {
      if(!pMasqueradingVector[0].empty())
      {
          if(pJson.isMember(pMasqueradingVector[0]))
          {
              if(!validJsonOfField(0, pMasqueradingVector[0], pJson[pMasqueradingVector[0]], err, true))
                  return false;
          }
      }
      if(!pMasqueradingVector[1].empty())
      {
          if(pJson.isMember(pMasqueradingVector[1]))
          {
              if(!validJsonOfField(1, pMasqueradingVector[1], pJson[pMasqueradingVector[1]], err, true))
                  return false;
          }
        else
        {
            err="The " + pMasqueradingVector[1] + " column cannot be null";
            return false;
        }
      }
      if(!pMasqueradingVector[2].empty())
      {
          if(pJson.isMember(pMasqueradingVector[2]))
          {
              if(!validJsonOfField(2, pMasqueradingVector[2], pJson[pMasqueradingVector[2]], err, true))
                  return false;
          }
      }
    }
    catch(const Json::LogicError &e) 
    {
      err = e.what();
      return false;
    }
    return true;
}
bool ApiImageTest::validateJsonForUpdate(const Json::Value &pJson, std::string &err)
{
    if(pJson.isMember("id"))
    {
        if(!validJsonOfField(0, "id", pJson["id"], err, false))
            return false;
    }
    else
    {
        err = "The value of primary key must be set in the json object for update";
        return false;
    }
    if(pJson.isMember("device_name"))
    {
        if(!validJsonOfField(1, "device_name", pJson["device_name"], err, false))
            return false;
    }
    if(pJson.isMember("img_url"))
    {
        if(!validJsonOfField(2, "img_url", pJson["img_url"], err, false))
            return false;
    }
    return true;
}
bool ApiImageTest::validateMasqueradedJsonForUpdate(const Json::Value &pJson,
                                                    const std::vector<std::string> &pMasqueradingVector,
                                                    std::string &err)
{
    if(pMasqueradingVector.size() != 3)
    {
        err = "Bad masquerading vector";
        return false;
    }
    try {
      if(!pMasqueradingVector[0].empty() && pJson.isMember(pMasqueradingVector[0]))
      {
          if(!validJsonOfField(0, pMasqueradingVector[0], pJson[pMasqueradingVector[0]], err, false))
              return false;
      }
    else
    {
        err = "The value of primary key must be set in the json object for update";
        return false;
    }
      if(!pMasqueradingVector[1].empty() && pJson.isMember(pMasqueradingVector[1]))
      {
          if(!validJsonOfField(1, pMasqueradingVector[1], pJson[pMasqueradingVector[1]], err, false))
              return false;
      }
      if(!pMasqueradingVector[2].empty() && pJson.isMember(pMasqueradingVector[2]))
      {
          if(!validJsonOfField(2, pMasqueradingVector[2], pJson[pMasqueradingVector[2]], err, false))
              return false;
      }
    }
    catch(const Json::LogicError &e) 
    {
      err = e.what();
      return false;
    }
    return true;
}
bool ApiImageTest::validJsonOfField(size_t index,
                                    const std::string &fieldName,
                                    const Json::Value &pJson, 
                                    std::string &err, 
                                    bool isForCreation)
{
    switch(index)
    {
        case 0:
            if(pJson.isNull())
            {
                err="The " + fieldName + " column cannot be null";
                return false;
            }
            if(isForCreation)
            {
                err="The automatic primary key cannot be set";
                return false;
            }        
            if(!pJson.isInt())
            {
                err="Type error in the "+fieldName+" field";
                return false;
            }
            break;
        case 1:
            if(pJson.isNull())
            {
                err="The " + fieldName + " column cannot be null";
                return false;
            }
            if(!pJson.isString())
            {
                err="Type error in the "+fieldName+" field";
                return false;                
            }
            // asString().length() creates a string object, is there any better way to validate the length?
            if(pJson.isString() && pJson.asString().length() > 64)
            {
                err="String length exceeds limit for the " +
                    fieldName +
                    " field (the maximum value is 64)";
                return false;               
            }

            break;
        case 2:
            if(pJson.isNull())
            {
                return true;
            }
            if(!pJson.isString())
            {
                err="Type error in the "+fieldName+" field";
                return false;                
            }
            // asString().length() creates a string object, is there any better way to validate the length?
            if(pJson.isString() && pJson.asString().length() > 100)
            {
                err="String length exceeds limit for the " +
                    fieldName +
                    " field (the maximum value is 100)";
                return false;               
            }

            break;
     
        default:
            err="Internal error in the server";
            return false;
            break;
    }
    return true;
}
/**
 *
 *  ApiImageTest.h
 *  DO NOT EDIT. This file is generated by drogon_ctl
 *
 */

#pragma once
#include <drogon/orm/Result.h>
#include <drogon/orm/Row.h>
#include <drogon/orm/Field.h>
#include <drogon/orm/SqlBinder.h>
#include <drogon/orm/Mapper.h>
#ifdef __cpp_impl_coroutine
#include <drogon/orm/CoroMapper.h>
#endif
#include <trantor/utils/Date.h>
#include <trantor/utils/Logger.h>
#include <json/json.h>
#include <string>
#include <memory>
#include <vector>
#include <tuple>
#include <stdint.h>
#include <iostream>

using namespace drogon::orm;
namespace drogon
{
namespace orm
{
class DbClient;
using DbClientPtr = std::shared_ptr<DbClient>;
}
}
namespace drogon_model
{
namespace REST 
{

class ApiImageTest
{
  public:
    struct Cols
    {
        static const std::string _id;
        static const std::string _device_name;
        static const std::string _img_url;
    };

    const static int primaryKeyNumber;
    const static std::string tableName;
    const static bool hasPrimaryKey;
    const static std::string primaryKeyName;
    using PrimaryKeyType = int32_t;
    const PrimaryKeyType &getPrimaryKey() const;

    /**
     * @brief constructor
     * @param r One row of records in the SQL query result.
     * @param indexOffset Set the offset to -1 to access all columns by column names, 
     * otherwise access all columns by offsets.
     * @note If the SQL is not a style of 'select * from table_name ...' (select all 
     * columns by an asterisk), please set the offset to -1.
     */
    explicit ApiImageTest(const Row &r, const ssize_t indexOffset = 0) noexcept;

    /**
     * @brief constructor
     * @param pJson The json object to construct a new instance.
     */
    explicit ApiImageTest(const Json::Value &pJson) noexcept(false);

    /**
     * @brief constructor
     * @param pJson The json object to construct a new instance.
     * @param pMasqueradingVector The aliases of table columns.
     */
    ApiImageTest(const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false);

    ApiImageTest() = default;
    
    void updateByJson(const Json::Value &pJson) noexcept(false);
    void updateByMasqueradedJson(const Json::Value &pJson,
                                 const std::vector<std::string> &pMasqueradingVector) noexcept(false);
    static bool validateJsonForCreation(const Json::Value &pJson, std::string &err);
    static bool validateMasqueradedJsonForCreation(const Json::Value &,
                                                const std::vector<std::string> &pMasqueradingVector,
                                                    std::string &err);
    static bool validateJsonForUpdate(const Json::Value &pJson, std::string &err);
    static bool validateMasqueradedJsonForUpdate(const Json::Value &,
                                          const std::vector<std::string> &pMasqueradingVector,
                                          std::string &err);
    static bool validJsonOfField(size_t index,
                          const std::string &fieldName,
                          const Json::Value &pJson, 
                          std::string &err, 
                          bool isForCreation);

    /**  For column id  */
    ///Get the value of the column id, returns the default value if the column is null
    const int32_t &getValueOfId() const noexcept;
    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
    const std::shared_ptr<int32_t> &getId() const noexcept;

    ///Set the value of the column id
    void setId(const int32_t &pId) noexcept;


    /**  For column device_name  */
    ///Get the value of the column device_name, returns the default value if the column is null
    const std::string &getValueOfDeviceName() const noexcept;
    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
    const std::shared_ptr<std::string> &getDeviceName() const noexcept;

    ///Set the value of the column device_name
    void setDeviceName(const std::string &pDeviceName) noexcept;
    void setDeviceName(std::string &&pDeviceName) noexcept;


    /**  For column img_url  */
    ///Get the value of the column img_url, returns the default value if the column is null
    const std::string &getValueOfImgUrl() const noexcept;
    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
    const std::shared_ptr<std::string> &getImgUrl() const noexcept;

    ///Set the value of the column img_url
    void setImgUrl(const std::string &pImgUrl) noexcept;
    void setImgUrl(std::string &&pImgUrl) noexcept;
    void setImgUrlToNull() noexcept;



    static size_t getColumnNumber() noexcept {  return 3;  }
    static const std::string &getColumnName(size_t index) noexcept(false);

    Json::Value toJson() const;
    Json::Value toMasqueradedJson(const std::vector<std::string> &pMasqueradingVector) const;
    /// Relationship interfaces
  private:
    friend Mapper<ApiImageTest>;
#ifdef __cpp_impl_coroutine
    friend CoroMapper<ApiImageTest>;
#endif
    static const std::vector<std::string> &insertColumns() noexcept;
    void outputArgs(drogon::orm::internal::SqlBinder &binder) const;
    const std::vector<std::string> updateColumns() const;
    void updateArgs(drogon::orm::internal::SqlBinder &binder) const;
    ///For mysql or sqlite3
    void updateId(const uint64_t id);
    std::shared_ptr<int32_t> id_;
    std::shared_ptr<std::string> deviceName_;
    std::shared_ptr<std::string> imgUrl_;
    struct MetaData
    {
        const std::string colName_;
        const std::string colType_;
        const std::string colDatabaseType_;
        const ssize_t colLength_;
        const bool isAutoVal_;
        const bool isPrimaryKey_;
        const bool notNull_;
    };
    static const std::vector<MetaData> metaData_;
    bool dirtyFlag_[3]={ false };
  public:
    static const std::string &sqlForFindingByPrimaryKey()
    {
        static const std::string sql="select * from " + tableName + " where id = ?";
        return sql;                   
    }

    static const std::string &sqlForDeletingByPrimaryKey()
    {
        static const std::string sql="delete from " + tableName + " where id = ?";
        return sql;                   
    }
    std::string sqlForInserting(bool &needSelection) const
    {
        std::string sql="insert into " + tableName + " (";
        size_t parametersCount = 0;
        needSelection = false;
            sql += "id,";
            ++parametersCount;
        if(dirtyFlag_[1])
        {
            sql += "device_name,";
            ++parametersCount;
        }
        if(dirtyFlag_[2])
        {
            sql += "img_url,";
            ++parametersCount;
        }
        needSelection=true;
        if(parametersCount > 0)
        {
            sql[sql.length()-1]=')';
            sql += " values (";
        }
        else
            sql += ") values (";
        
        sql +="default,";
        if(dirtyFlag_[1])
        {
            sql.append("?,");

        } 
        if(dirtyFlag_[2])
        {
            sql.append("?,");

        } 
        if(parametersCount > 0)
        {
            sql.resize(sql.length() - 1);
        }
        sql.append(1, ')');
        LOG_TRACE << sql;
        return sql;   
    }
};
} // namespace REST
} // namespace drogon_model

接下来就可以在项目中使用各个创建的Model类了,即可以使用ApiImageTest类了,对应的表为api_image_test。

C++ web框架drogon 使用对象关系映射ORM(Object Relational Mapping)模式

该表包含id、device_name和img_url三个字段,分别对应ApiImageTest类中的三个属性。

Model的使用

有两种方式来利用Model来访问、修改数据库,一是直接Model类的接口,二是使用Mapper类模板,但是很遗憾,我在使用Model类接口是一直没有获取到数据库的相关信息,我们致力使用Mapper类模板来进行展示。

	//控制台中文异常
	std::system("chcp 65001");

	auto clientPtr = drogon::app().getDbClient();
	//同步查询
	Mapper<ApiImageTest> mp(clientPtr);
	std::vector<ApiImageTest> news_list = mp.orderBy(ApiImageTest::Cols::_id).limit(15).offset(0).findAll();
	std::cout << news_list.size() << " rows!" << std::endl;

当然你需要在使用ApiImageTest类的地方导入头文件

#include "../models/ApiImageTest.h"

在项目中运行此段代码后会打印出api_image_test表数据的行数。

运行效果图如下,数据库中有有15条数据。

C++ web框架drogon 使用对象关系映射ORM(Object Relational Mapping)模式

上一篇:基于SSH框架的人力资源管理系统设计与实现


下一篇:elasticsearch中动态模板全解实战