The C++ web framework dragon uses the ORM (Object Relational Mapping) pattern

Posted by CharlesH on Sun, 23 Jan 2022 11:22:39 +0100

In the drogon framework, the ORM function is added. Using many SQL statements embedded in C + + code not only makes the code messy and ugly, but also requires good database statement writing skills. Using ORM, the operation database can be transformed into operation objects. In ORM, a table usually corresponds to a class, and each instance of a class corresponds to a record in the table, Each attribute of the class corresponds to each field in the table.  

ORM provides the mapping to the database. You don't need to write SQL code directly. You only need to operate the object to operate the data on the database.

It allows software developers to focus on the processing of business logic and improves the development efficiency.

Model

To use Drogon's ORM support, first create a Model class, Drogon's command-line program drogon_ctl provides the function of generating Model classes. It reads table information from the database specified by the user, and automatically generates source files of multiple Model classes according to this information. When using the Model, you can include the corresponding header file.

Obviously, each Model class corresponds to a specific database table, and each instance of Model class corresponds to a row of records in the table.

The tables configuration item is unique to it. It is a string array. Each string represents the table name to be converted to Model. If the item is empty, all tables will generate the corresponding Model class.

With drogon_ The models directory and the corresponding Model have been created in advance under the project directory created by the CTL create project command JSON file, users can edit the configuration file and use drogon_ The CTL command creates a Model class

The creation commands of model class are as follows:

drogon_ctl create model 

The last parameter is the path where the model is stored. There must be a configuration file model JSON to configure the dragon_ Generally, the connection parameters from CTL to the database directly use the model under the models directory json. It is a file in JSON format and supports comments. Examples are as follows:

{
    //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
    }
}

In the parameters, you need to fill in the corresponding configuration data of the database, including database address, password, database name and table name. The database and table here must already exist in the database. It is not used with Django ORM. The database will not be created automatically, but a new class will be created for the created database under the modules directory.

Use the following instructions:

drongo_ctl create model modles

After execution, it will be generated in the models folder cc and h file to create a file for the class corresponding to the table

/**
 *
 *  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

Next, you can use each created Model class in the project, that is, you can use the ApiImageTest class, and the corresponding table is api_image_test.

The table contains id and device_name and img_ The three fields of URL correspond to the three properties in the ApiImageTest class.

Use of Model

There are two ways to use the Model to access and modify the database. One is to directly use the Model class interface, and the other is to use the Mapper class template. Unfortunately, I have not obtained the relevant information of the database when using the Model class interface. We are committed to using the Mapper class template for display.

	//Console Chinese exception
	std::system("chcp 65001");

	auto clientPtr = drogon::app().getDbClient();
	//Synchronous query
	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;

Of course, you need to import the header file where you use the ApiImageTest class

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

After running this code in the project, the API will print out_ image_ Number of rows of test table data.

The operation effect diagram is as follows. There are 15 pieces of data in the database.