Mapper template works

This commit is contained in:
antao 2018-10-30 14:51:15 +08:00 committed by an-tao
parent 6385330c0f
commit 85664b86fc
8 changed files with 502 additions and 28 deletions

View File

@ -39,13 +39,12 @@ class Criteria
{
public:
explicit operator bool() const { return !_condString.empty(); }
std::string criteriaString() { return _condString; }
std::string criteriaString() const { return _condString; }
template <typename T>
Criteria(const std::string &colName, const CompareOperator &opera, T &&arg)
{
static_assert(opera != CompareOperator::IsNotNull &&
opera != CompareOperator::IsNull,
"Invalid compare operator!");
assert(opera != CompareOperator::IsNotNull &&
opera != CompareOperator::IsNull);
_condString = colName;
switch (opera)
{
@ -72,7 +71,7 @@ class Criteria
default:
break;
}
_outputArgumentsFunc = [=](inner::SqlBinder &binder) {
_outputArgumentsFunc = [=](internal::SqlBinder &binder) {
binder << arg;
};
}
@ -100,7 +99,7 @@ class Criteria
}
}
Criteria() {}
void outputArgs(internal::SqlBinder &binder)
void outputArgs(internal::SqlBinder &binder) const
{
if (_outputArgumentsFunc)
_outputArgumentsFunc(binder);

View File

@ -31,7 +31,7 @@ namespace orm
{
typedef std::function<void(const Result &)> ResultCallback;
typedef std::function<void(const std::exception_ptr &)> ExceptionCallback;
typedef std::function<void(const DrogonDbException &)> ExceptionCallback;
class DbClient : public trantor::NonCopyable
{

View File

@ -14,6 +14,7 @@
#pragma once
#include <drogon/orm/DbClient.h>
#include <drogon/orm/Criteria.h>
#include <drogon/utils/Utilities.h>
namespace drogon
{
@ -36,15 +37,15 @@ class Mapper
typedef std::function<void(T)> SingleRowCallback;
typedef std::function<void(std::vector<T>)> MultipleRowsCallback;
typedef std::function<void(const sise_t)> CountCallback;
typedef std::function<void(const size_t)> CountCallback;
Mapper(const DbClient &client) : _client(client) {}
Mapper(DbClient &client) : _client(client) {}
T findByPrimaryKey(const T::PrimaryKeyType &key) noexcept(false);
void findByPrimaryKey(const T::PrimaryKeyType &key,
T findByPrimaryKey(const typename T::PrimaryKeyType &key) noexcept(false);
void findByPrimaryKey(const typename T::PrimaryKeyType &key,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept;
std::future<T> findFutureByPrimaryKey(const T::PrimaryKeyType &key) noexcept;
std::future<T> findFutureByPrimaryKey(const typename T::PrimaryKeyType &key) noexcept;
std::vector<T> findAll() noexcept(false);
void findAll(const MultipleRowsCallback &rcb,
@ -55,7 +56,7 @@ class Mapper
void count(const Criteria &criteria,
const CountCallback &rcb,
const ExceptionCallback &ecb) noexcept;
std::future<size_t> count(const Criteria &criteria) noexcept;
std::future<size_t> countFuture(const Criteria &criteria) noexcept;
T findOne(const Criteria &criteria) noexcept(false);
void findOne(const Criteria &criteria,
@ -73,28 +74,36 @@ class Mapper
void insert(const T &obj,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept;
std::future<T> insert(const T &) noexcept;
std::future<T> insertFuture(const T &) noexcept;
size_t update(T &obj) noexcept(false);
void update(const T &obj,
const CountCallback &rcb,
const ExceptionCallback &ecb) noexcept;
std::future<size_t> update(const T &obj) noexcept;
std::future<size_t> updateFuture(const T &obj) noexcept;
private:
Dbclient &_client;
std::string getSqlForFindingByPrimaryKey();
DbClient &_client;
std::string _limitString;
std::string _offsetString;
std::string _orderbyString;
void clear()
{
_limitString.clear();
_offsetString.clear();
_orderbyString.clear();
}
};
template <typename T>
inline T Mapper<T>::findByPrimaryKey(const T::PrimaryKeyType &key) noexcept(false)
inline T Mapper<T>::findByPrimaryKey(const typename T::PrimaryKeyType &key) noexcept(false)
{
static_assert(T::hasPrimaryKey, "No primary key in the table!");
return findOne(Criteria(T::primaryKeyName, key));
}
template <typename T>
inline void Mapper<T>::findByPrimaryKey(const T::PrimaryKeyType &key,
inline void Mapper<T>::findByPrimaryKey(const typename T::PrimaryKeyType &key,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept
{
@ -103,7 +112,7 @@ inline void Mapper<T>::findByPrimaryKey(const T::PrimaryKeyType &key,
}
template <typename T>
inline std::future<T> Mapper<T>::findFutureByPrimaryKey(const T::PrimaryKeyType &key) noexcept
inline std::future<T> Mapper<T>::findFutureByPrimaryKey(const typename T::PrimaryKeyType &key) noexcept
{
static_assert(T::hasPrimaryKey, "No primary key in the table!");
return findFutureOne(Criteria(T::primaryKeyName, key));
@ -120,6 +129,8 @@ inline T Mapper<T>::findOne(const Criteria &criteria) noexcept(false)
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
sql.append(_orderbyString).append(_offsetString).append(_limitString);
clear();
Result r(nullptr);
{
auto binder = _client << sql;
@ -156,7 +167,8 @@ inline void Mapper<T>::findOne(const Criteria &criteria,
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
sql.append(_orderbyString).append(_offsetString).append(_limitString);
clear();
auto binder = _client << sql;
if (criteria)
criteria.outputArgs(binder);
@ -167,7 +179,7 @@ inline void Mapper<T>::findOne(const Criteria &criteria,
}
else if (r.size() > 1)
{
ech(Failure("Found more than one row"));
ecb(Failure("Found more than one row"));
}
else
{
@ -188,7 +200,8 @@ inline std::future<T> Mapper<T>::findFutureOne(const Criteria &criteria) noexcep
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
sql.append(_orderbyString).append(_offsetString).append(_limitString);
clear();
auto binder = _client << sql;
if (criteria)
criteria.outputArgs(binder);
@ -228,6 +241,427 @@ inline std::future<T> Mapper<T>::findFutureOne(const Criteria &criteria) noexcep
binder.exec();
return prom->get_future();
}
template <typename T>
inline std::vector<T> Mapper<T>::findBy(const Criteria &criteria) noexcept(false)
{
std::string sql = "select * from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
sql.append(_orderbyString).append(_offsetString).append(_limitString);
clear();
Result r(nullptr);
{
auto binder = _client << sql;
if (criteria)
criteria.outputArgs(binder);
binder << Mode::Blocking;
binder >> [&r](const Result &result) {
r = result;
};
binder.exec(); //exec may be throw exception;
}
std::vector<T> ret;
for (auto row : r)
{
ret.push_back(T(row));
}
return ret;
}
template <typename T>
inline void Mapper<T>::findBy(const Criteria &criteria,
const MultipleRowsCallback &rcb,
const ExceptionCallback &ecb) noexcept
{
std::string sql = "select * from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
sql.append(_orderbyString).append(_offsetString).append(_limitString);
clear();
auto binder = _client << sql;
if (criteria)
criteria.outputArgs(binder);
binder >> [=](const Result &r) {
std::vector<T> ret;
for (auto row : r)
{
ret.push_back(T(row));
}
rcb(ret);
};
binder >> ecb;
}
template <typename T>
inline std::future<std::vector<T>> Mapper<T>::findFutureBy(const Criteria &criteria) noexcept
{
std::string sql = "select * from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
sql.append(_orderbyString).append(_offsetString).append(_limitString);
clear();
auto binder = _client << sql;
if (criteria)
criteria.outputArgs(binder);
std::shared_ptr<std::promise<std::vector<T>>> prom = std::make_shared<std::promise<std::vector<T>>>();
binder >> [=](const Result &r) {
std::vector<T> ret;
for (auto row : r)
{
ret.push_back(T(row));
}
prom->set_value(ret);
};
binder >> [=](const std::exception_ptr &e) {
prom->set_exception(e);
};
binder.exec();
return prom->get_future();
}
template <typename T>
inline std::vector<T> Mapper<T>::findAll() noexcept(false)
{
return findBy(Criteria());
}
template <typename T>
inline void Mapper<T>::findAll(const MultipleRowsCallback &rcb,
const ExceptionCallback &ecb) noexcept
{
findBy(Criteria(), rcb, ecb);
}
template <typename T>
inline std::future<std::vector<T>> Mapper<T>::findFutureAll() noexcept
{
return findFutureBy(Criteria());
}
template <typename T>
inline size_t Mapper<T>::count(const Criteria &criteria) noexcept(false)
{
std::string sql = "select count(*) from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
clear();
Result r(nullptr);
{
auto binder = _client << sql;
if (criteria)
criteria.outputArgs(binder);
binder << Mode::Blocking;
binder >> [&r](const Result &result) {
r = result;
};
binder.exec(); //exec may be throw exception;
}
assert(r.size() == 1);
return r[0]["count"].as<size_t>();
}
template <typename T>
inline void Mapper<T>::count(const Criteria &criteria,
const CountCallback &rcb,
const ExceptionCallback &ecb) noexcept
{
std::string sql = "select count(*) from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
clear();
auto binder = _client << sql;
if (criteria)
criteria.outputArgs(binder);
binder >> [=](const Result &r) {
assert(r.size() == 1);
rcb(r[0]["count"].as<size_t>());
};
binder >> ecb;
}
template <typename T>
inline std::future<size_t> Mapper<T>::countFuture(const Criteria &criteria) noexcept
{
std::string sql = "select count(*) from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
clear();
auto binder = _client << sql;
if (criteria)
criteria.outputArgs(binder);
std::shared_ptr<std::promise<size_t>> prom = std::make_shared<std::promise<size_t>>();
binder >> [=](const Result &r) {
assert(r.size() == 1);
prom->set_value(r[0]["count"].as<size_t>());
};
binder >> [=](const std::exception_ptr &e) {
prom->set_exception(e);
};
binder.exec();
return prom->get_future();
}
template <typename T>
inline void Mapper<T>::insert(T &obj) noexcept(false)
{
clear();
std::string sql = "insert into ";
sql += T::tableName;
sql += " (";
for (auto colName : T::insertColumns())
{
sql += colName;
sql += ",";
}
sql[sql.length() - 1] = ')'; //Replace the last ','
sql += "values (";
for (int i = 0; i < T::insertColumns().size(); i++)
{
sql += "$?,";
}
sql[sql.length() - 1] = ')'; //Replace the last ','
sql += " returning *";
_client.replaceSqlPlaceHolder(sql, "$?");
Result r(nullptr);
{
auto binder = _client << sql;
obj.outputArgs(binder);
binder << Mode::Blocking;
binder >> [&r](const Result &result) {
r = result;
};
binder.exec(); //Maybe throw exception;
}
assert(r.szie() == 1);
obj = T(r[0]);
}
template <typename T>
inline void Mapper<T>::insert(const T &obj,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept
{
clear();
std::string sql = "insert into ";
sql += T::tableName;
sql += " (";
for (auto colName : T::insertColumns())
{
sql += colName;
sql += ",";
}
sql[sql.length() - 1] = ')'; //Replace the last ','
sql += "values (";
for (int i = 0; i < T::insertColumns().size(); i++)
{
sql += "$?,";
}
sql[sql.length() - 1] = ')'; //Replace the last ','
sql += " returning *";
_client.replaceSqlPlaceHolder(sql, "$?");
auto binder = _client << sql;
obj.outputArgs(binder);
binder >> [=](const Result &r) {
assert(r.size() == 1);
rcb(T(r[0]));
};
binder >> ecb;
}
template <typename T>
inline std::future<T> Mapper<T>::insertFuture(const T &obj) noexcept
{
clear();
std::string sql = "insert into ";
sql += T::tableName;
sql += " (";
for (auto colName : T::insertColumns())
{
sql += colName;
sql += ",";
}
sql[sql.length() - 1] = ')'; //Replace the last ','
sql += "values (";
for (int i = 0; i < T::insertColumns().size(); i++)
{
sql += "$?,";
}
sql[sql.length() - 1] = ')'; //Replace the last ','
sql += " returning *";
_client.replaceSqlPlaceHolder(sql, "$?");
auto binder = _client << sql;
obj.outputArgs(binder);
std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>();
binder >> [=](const Result &r) {
assert(r.size() == 1);
prom->set_value(T(r[0]));
};
binder >> [=](const std::exception_ptr &e) {
prom->set_exception(e);
};
binder.exec();
return prom->get_future();
}
template <typename T>
inline size_t Mapper<T>::update(T &obj) noexcept(false)
{
clear();
static_assert(T::hasPrimaryKey, "No primary key in the table!");
std::string sql = "update ";
sql += T::tableName;
sql += " set ";
for (auto colName : T::insertColumns())
{
sql += colName;
sql += " = $?,";
}
sql[sql.length() - 1] = ' '; //Replace the last ','
sql += "where ";
sql += T::primaryKeyName;
sql += " = $?";
_client.replaceSqlPlaceHolder(sql, "$?");
Result r(nullptr);
{
auto binder = _client << sql;
obj.outputArgs(binder);
binder << obj.getPrimaryKey();
binder << Mode::Blocking;
binder >> [&r](const Result &result) {
r = result;
};
binder.exec(); //Maybe throw exception;
}
return r.affectedRows();
}
template <typename T>
inline void Mapper<T>::update(const T &obj,
const CountCallback &rcb,
const ExceptionCallback &ecb) noexcept
{
clear();
static_assert(T::hasPrimaryKey, "No primary key in the table!");
std::string sql = "update ";
sql += T::tableName;
sql += " set ";
for (auto colName : T::insertColumns())
{
sql += colName;
sql += " = $?,";
}
sql[sql.length() - 1] = ' '; //Replace the last ','
sql += "where ";
sql += T::primaryKeyName;
sql += " = $?";
_client.replaceSqlPlaceHolder(sql, "$?");
auto binder = _client << sql;
obj.outputArgs(binder);
binder << obj.getPrimaryKey();
binder >> [=](const Result &r) {
rcb(r.affectedRows());
};
binder >> ecb;
}
template <typename T>
inline std::future<size_t> Mapper<T>::updateFuture(const T &obj) noexcept
{
clear();
static_assert(T::hasPrimaryKey, "No primary key in the table!");
std::string sql = "update ";
sql += T::tableName;
sql += " set ";
for (auto colName : T::insertColumns())
{
sql += colName;
sql += " = $?,";
}
sql[sql.length() - 1] = ' '; //Replace the last ','
sql += "where ";
sql += T::primaryKeyName;
sql += " = $?";
_client.replaceSqlPlaceHolder(sql, "$?");
auto binder = _client << sql;
obj.outputArgs(binder);
binder << obj.getPrimaryKey();
std::shared_ptr<std::promise<size_t>> prom = std::make_shared<std::promise<size_t>>();
binder >> [=](const Result &r) {
prom->set_value(r.affectedRows());
};
binder >> [=](const std::exception_ptr &e) {
prom->set_exception(e);
};
binder.exec();
return prom->get_future();
}
template <typename T>
inline Mapper<T> &Mapper<T>::limit(size_t limit)
{
assert(limit > 0);
if (limit > 0)
{
_limitString = formattedString(" limit %u", limit);
}
return *this;
}
template <typename T>
inline Mapper<T> &Mapper<T>::offset(size_t offset)
{
_offsetString = formattedString(" offset %u", offset);
return *this;
}
template <typename T>
inline Mapper<T> &Mapper<T>::orderBy(const std::string &colName, const SortOrder &order)
{
if (_orderbyString.empty())
{
_orderbyString = formattedString(" order by %s", colName.c_str());
if (order == SortOrder::DESC)
{
_orderbyString += " desc";
}
}
else
{
_orderbyString += ",";
_orderbyString += colName;
if (order == SortOrder::DESC)
{
_orderbyString += " desc";
}
}
return *this;
}
template <typename T>
inline Mapper<T> &Mapper<T>::orderBy(size_t colIndex, const SortOrder &order)
{
std::string colName = T::getColumnName(colIndex);
assert(!colName.empty());
return orderBy(colName, order);
}
} // namespace orm
} // namespace drogon

View File

@ -32,7 +32,7 @@ namespace orm
class DbClient;
typedef std::function<void(const Result &)> QueryCallback;
typedef std::function<void(const std::exception_ptr &)> ExceptCallback;
typedef std::function<void(const std::exception_ptr &)> ExceptPtrCallback;
enum class Mode
{
NonBlocking,
@ -267,7 +267,7 @@ class SqlBinder
Mode _mode = Mode::NonBlocking;
std::shared_ptr<CallbackHolderBase> _callbackHolder;
DrogonDbExceptionCallback _exceptCallback;
ExceptCallback _exceptPtrCallback;
ExceptPtrCallback _exceptPtrCallback;
bool _execed = false;
bool _destructed = false;
bool _isExceptPtr = false;

View File

@ -133,7 +133,7 @@ void PgClientImpl::execSql(const std::string &sql,
const std::vector<int> &length,
const std::vector<int> &format,
const QueryCallback &cb,
const ExceptCallback &exceptCb)
const ExceptPtrCallback &exceptCb)
{
assert(paraNum == parameters.size());
assert(paraNum == length.size());

View File

@ -67,7 +67,7 @@ class PgClientImpl : public trantor::NonCopyable
std::vector<std::string> _parameters;
std::vector<int> _format;
QueryCallback _cb;
ExceptCallback _exceptCb;
ExceptPtrCallback _exceptCb;
};
std::list<SqlCmd> _sqlCmdBuffer;
std::mutex _bufferMutex;

View File

@ -1,3 +1,4 @@
link_libraries(drogon trantor pthread dl)
add_executable(test1 test1.cc)
add_executable(test1 test1.cc)
add_executable(test2 test2.cc)

View File

@ -0,0 +1,40 @@
#include <drogon/orm/PgClient.h>
#include <drogon/orm/Mapper.h>
#include <trantor/utils/Logger.h>
#include <iostream>
#include <unistd.h>
#include <string>
using namespace drogon::orm;
class User
{
public:
const static std::string primaryKeyName;
const static bool hasPrimaryKey;
const static std::string tableName;
typedef int PrimaryKeyType;
User(const Row &r)
: _userId(r["user_id"].as<std::string>()),
_userName(r["user_name"].as<std::string>())
{
}
std::string _userId;
std::string _userName;
};
const std::string User::primaryKeyName = "user_uuid";
const bool User::hasPrimaryKey = true;
const std::string User::tableName = "users";
int main()
{
drogon::orm::PgClient client("host=127.0.0.1 port=5432 dbname=trantor user=antao", 1);
LOG_DEBUG << "start!";
sleep(1);
Mapper<User> mapper(client);
auto U = mapper.findByPrimaryKey(2);
std::cout << "id=" << U._userId << std::endl;
std::cout << "name=" << U._userName << std::endl;
getchar();
}