From 85664b86fc11befd96118b50f892c48af6bc6488 Mon Sep 17 00:00:00 2001 From: antao Date: Tue, 30 Oct 2018 14:51:15 +0800 Subject: [PATCH] Mapper template works --- orm_lib/inc/drogon/orm/Criteria.h | 11 +- orm_lib/inc/drogon/orm/DbClient.h | 2 +- orm_lib/inc/drogon/orm/Mapper.h | 466 +++++++++++++++++- orm_lib/inc/drogon/orm/SqlBinder.h | 4 +- orm_lib/src/postgresql_impl/PgClientImpl.cc | 2 +- orm_lib/src/postgresql_impl/PgClientImpl.h | 2 +- .../src/postgresql_impl/test/CMakeLists.txt | 3 +- orm_lib/src/postgresql_impl/test/test2.cc | 40 ++ 8 files changed, 502 insertions(+), 28 deletions(-) create mode 100644 orm_lib/src/postgresql_impl/test/test2.cc diff --git a/orm_lib/inc/drogon/orm/Criteria.h b/orm_lib/inc/drogon/orm/Criteria.h index ffb9a75d..d9f1025e 100644 --- a/orm_lib/inc/drogon/orm/Criteria.h +++ b/orm_lib/inc/drogon/orm/Criteria.h @@ -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 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); diff --git a/orm_lib/inc/drogon/orm/DbClient.h b/orm_lib/inc/drogon/orm/DbClient.h index 8cff4957..abfe7b7e 100644 --- a/orm_lib/inc/drogon/orm/DbClient.h +++ b/orm_lib/inc/drogon/orm/DbClient.h @@ -31,7 +31,7 @@ namespace orm { typedef std::function ResultCallback; -typedef std::function ExceptionCallback; +typedef std::function ExceptionCallback; class DbClient : public trantor::NonCopyable { diff --git a/orm_lib/inc/drogon/orm/Mapper.h b/orm_lib/inc/drogon/orm/Mapper.h index dad79f6d..84047a6e 100644 --- a/orm_lib/inc/drogon/orm/Mapper.h +++ b/orm_lib/inc/drogon/orm/Mapper.h @@ -14,6 +14,7 @@ #pragma once #include #include +#include namespace drogon { @@ -36,15 +37,15 @@ class Mapper typedef std::function SingleRowCallback; typedef std::function)> MultipleRowsCallback; - typedef std::function CountCallback; + typedef std::function 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 findFutureByPrimaryKey(const T::PrimaryKeyType &key) noexcept; + std::future findFutureByPrimaryKey(const typename T::PrimaryKeyType &key) noexcept; std::vector 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 count(const Criteria &criteria) noexcept; + std::future 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 insert(const T &) noexcept; + std::future 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 update(const T &obj) noexcept; + std::future 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 -inline T Mapper::findByPrimaryKey(const T::PrimaryKeyType &key) noexcept(false) +inline T Mapper::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 -inline void Mapper::findByPrimaryKey(const T::PrimaryKeyType &key, +inline void Mapper::findByPrimaryKey(const typename T::PrimaryKeyType &key, const SingleRowCallback &rcb, const ExceptionCallback &ecb) noexcept { @@ -103,7 +112,7 @@ inline void Mapper::findByPrimaryKey(const T::PrimaryKeyType &key, } template -inline std::future Mapper::findFutureByPrimaryKey(const T::PrimaryKeyType &key) noexcept +inline std::future Mapper::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::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::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::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 Mapper::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 Mapper::findFutureOne(const Criteria &criteria) noexcep binder.exec(); return prom->get_future(); } +template +inline std::vector Mapper::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 ret; + for (auto row : r) + { + ret.push_back(T(row)); + } + return ret; +} +template +inline void Mapper::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 ret; + for (auto row : r) + { + ret.push_back(T(row)); + } + rcb(ret); + }; + binder >> ecb; +} +template +inline std::future> Mapper::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>> prom = std::make_shared>>(); + binder >> [=](const Result &r) { + std::vector 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 +inline std::vector Mapper::findAll() noexcept(false) +{ + return findBy(Criteria()); +} +template +inline void Mapper::findAll(const MultipleRowsCallback &rcb, + const ExceptionCallback &ecb) noexcept +{ + findBy(Criteria(), rcb, ecb); +} +template +inline std::future> Mapper::findFutureAll() noexcept +{ + return findFutureBy(Criteria()); +} +template +inline size_t Mapper::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(); +} +template +inline void Mapper::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()); + }; + binder >> ecb; +} +template +inline std::future Mapper::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> prom = std::make_shared>(); + binder >> [=](const Result &r) { + assert(r.size() == 1); + prom->set_value(r[0]["count"].as()); + }; + binder >> [=](const std::exception_ptr &e) { + prom->set_exception(e); + }; + binder.exec(); + return prom->get_future(); +} +template +inline void Mapper::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 +inline void Mapper::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 +inline std::future Mapper::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> prom = std::make_shared>(); + 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 +inline size_t Mapper::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 +inline void Mapper::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 +inline std::future Mapper::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> prom = std::make_shared>(); + 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 +inline Mapper &Mapper::limit(size_t limit) +{ + assert(limit > 0); + if (limit > 0) + { + _limitString = formattedString(" limit %u", limit); + } + return *this; +} +template +inline Mapper &Mapper::offset(size_t offset) +{ + _offsetString = formattedString(" offset %u", offset); + return *this; +} +template +inline Mapper &Mapper::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 +inline Mapper &Mapper::orderBy(size_t colIndex, const SortOrder &order) +{ + std::string colName = T::getColumnName(colIndex); + assert(!colName.empty()); + return orderBy(colName, order); +} } // namespace orm } // namespace drogon diff --git a/orm_lib/inc/drogon/orm/SqlBinder.h b/orm_lib/inc/drogon/orm/SqlBinder.h index d226a37e..cbafbf5e 100644 --- a/orm_lib/inc/drogon/orm/SqlBinder.h +++ b/orm_lib/inc/drogon/orm/SqlBinder.h @@ -32,7 +32,7 @@ namespace orm class DbClient; typedef std::function QueryCallback; -typedef std::function ExceptCallback; +typedef std::function ExceptPtrCallback; enum class Mode { NonBlocking, @@ -267,7 +267,7 @@ class SqlBinder Mode _mode = Mode::NonBlocking; std::shared_ptr _callbackHolder; DrogonDbExceptionCallback _exceptCallback; - ExceptCallback _exceptPtrCallback; + ExceptPtrCallback _exceptPtrCallback; bool _execed = false; bool _destructed = false; bool _isExceptPtr = false; diff --git a/orm_lib/src/postgresql_impl/PgClientImpl.cc b/orm_lib/src/postgresql_impl/PgClientImpl.cc index 31154404..59ab6328 100644 --- a/orm_lib/src/postgresql_impl/PgClientImpl.cc +++ b/orm_lib/src/postgresql_impl/PgClientImpl.cc @@ -133,7 +133,7 @@ void PgClientImpl::execSql(const std::string &sql, const std::vector &length, const std::vector &format, const QueryCallback &cb, - const ExceptCallback &exceptCb) + const ExceptPtrCallback &exceptCb) { assert(paraNum == parameters.size()); assert(paraNum == length.size()); diff --git a/orm_lib/src/postgresql_impl/PgClientImpl.h b/orm_lib/src/postgresql_impl/PgClientImpl.h index e1c0c778..be3bc478 100644 --- a/orm_lib/src/postgresql_impl/PgClientImpl.h +++ b/orm_lib/src/postgresql_impl/PgClientImpl.h @@ -67,7 +67,7 @@ class PgClientImpl : public trantor::NonCopyable std::vector _parameters; std::vector _format; QueryCallback _cb; - ExceptCallback _exceptCb; + ExceptPtrCallback _exceptCb; }; std::list _sqlCmdBuffer; std::mutex _bufferMutex; diff --git a/orm_lib/src/postgresql_impl/test/CMakeLists.txt b/orm_lib/src/postgresql_impl/test/CMakeLists.txt index 9cbb22a7..6c1d7242 100644 --- a/orm_lib/src/postgresql_impl/test/CMakeLists.txt +++ b/orm_lib/src/postgresql_impl/test/CMakeLists.txt @@ -1,3 +1,4 @@ link_libraries(drogon trantor pthread dl) -add_executable(test1 test1.cc) \ No newline at end of file +add_executable(test1 test1.cc) +add_executable(test2 test2.cc) \ No newline at end of file diff --git a/orm_lib/src/postgresql_impl/test/test2.cc b/orm_lib/src/postgresql_impl/test/test2.cc new file mode 100644 index 00000000..71acab70 --- /dev/null +++ b/orm_lib/src/postgresql_impl/test/test2.cc @@ -0,0 +1,40 @@ +#include +#include + +#include +#include +#include +#include + +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()), + _userName(r["user_name"].as()) + { + } + 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 mapper(client); + auto U = mapper.findByPrimaryKey(2); + std::cout << "id=" << U._userId << std::endl; + std::cout << "name=" << U._userName << std::endl; + getchar(); +} \ No newline at end of file