Add Criteria class

This commit is contained in:
antao 2018-10-29 18:34:55 +08:00 committed by an-tao
parent 2dcb924c81
commit 054b0a2d68
7 changed files with 378 additions and 26 deletions

View File

@ -0,0 +1,164 @@
/**
*
* Criteria.h
* An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
*
*/
#include <drogon/config.h>
#include <drogon/orm/SqlBinder.h>
#include <string>
#include <tuple>
#include <memory>
#include <type_traits>
#include <assert.h>
namespace drogon
{
namespace orm
{
enum class CompareOperator
{
EQ,
NE,
GT,
GE,
LT,
LE,
IsNull,
IsNotNull
};
class Criteria
{
public:
explicit operator bool() const { return !_condString.empty(); }
std::string criteriaString() { 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!");
_condString = colName;
switch (opera)
{
case CompareOperator::EQ:
_condString += " = $?";
break;
case CompareOperator::NE:
_condString += " != $?";
break;
case CompareOperator::GT:
_condString += " > $?";
break;
case CompareOperator::GE:
_condString += " >= $?";
break;
case CompareOperator::LT:
_condString += " < $?";
break;
case CompareOperator::LE:
_condString += " <= $?";
break;
case CompareOperator::IsNull:
case CompareOperator::IsNotNull:
default:
break;
}
_outputArgumentsFunc = [=](inner::SqlBinder &binder) {
binder << arg;
};
}
template <typename T>
Criteria(const std::string &colName, T &&arg)
: Criteria(colName, CompareOperator::EQ, arg)
{
}
Criteria(const std::string &colName, const CompareOperator &opera)
{
assert(opera == CompareOperator::IsNotNull ||
opera == CompareOperator::IsNull);
_condString = colName;
switch (opera)
{
case CompareOperator::IsNull:
_condString += " is null";
break;
case CompareOperator::IsNotNull:
_condString += " is not null";
break;
default:
break;
}
}
Criteria() {}
void outputArgs(internal::SqlBinder &binder)
{
if (_outputArgumentsFunc)
_outputArgumentsFunc(binder);
}
private:
friend const Criteria operator&&(Criteria cond1, Criteria cond2);
friend const Criteria operator||(Criteria cond1, Criteria cond2);
std::string _condString;
std::function<void(internal::SqlBinder &)> _outputArgumentsFunc;
}; // namespace orm
const Criteria operator&&(Criteria cond1, Criteria cond2)
{
assert(cond1 && cond2);
Criteria cond;
cond._condString = "( ";
cond._condString += cond1._condString;
cond._condString += " ) and ( ";
cond._condString += cond2._condString;
cond._condString += " )";
auto cond1Ptr = std::make_shared<Criteria>(std::move(cond1));
auto cond2Ptr = std::make_shared<Criteria>(std::move(cond2));
cond._outputArgumentsFunc = [=](internal::SqlBinder &binder) {
if (cond1Ptr->_outputArgumentsFunc)
{
cond1Ptr->_outputArgumentsFunc(binder);
}
if (cond2Ptr->_outputArgumentsFunc)
{
cond2Ptr->_outputArgumentsFunc(binder);
}
};
return cond;
}
const Criteria operator||(Criteria cond1, Criteria cond2)
{
assert(cond1 && cond2);
Criteria cond;
cond._condString = "( ";
cond._condString += cond1._condString;
cond._condString += " ) or ( ";
cond._condString += cond2._condString;
cond._condString += " )";
auto cond1Ptr = std::make_shared<Criteria>(std::move(cond1));
auto cond2Ptr = std::make_shared<Criteria>(std::move(cond2));
cond._outputArgumentsFunc = [=](internal::SqlBinder &binder) {
if (cond1Ptr->_outputArgumentsFunc)
{
cond1Ptr->_outputArgumentsFunc(binder);
}
if (cond2Ptr->_outputArgumentsFunc)
{
cond2Ptr->_outputArgumentsFunc(binder);
}
};
return cond;
}
} // namespace orm
} // namespace drogon

View File

@ -96,8 +96,9 @@ public:
}
internal::SqlBinder operator<<(const std::string &sql);
virtual std::string replaceSqlPlaceHolder(const std::string &sqlStr, const std::string &holderStr) const = 0;
private:
private:
friend internal::SqlBinder;
virtual void execSql(const std::string &sql,
size_t paraNum,

View File

@ -13,18 +13,31 @@
#pragma once
#include <drogon/orm/DbClient.h>
#include <drogon/orm/Criteria.h>
namespace drogon
{
namespace orm
{
class Condition;
enum class SortOrder
{
ASC,
DESC
};
template <typename T>
class Mapper
{
public:
Mapper<T> &limit(size_t limit);
Mapper<T> &offset(size_t offset);
Mapper<T> &orderBy(const std::string &colName, const SortOrder &order = SortOrder::ASC);
Mapper<T> &orderBy(size_t colIndex, const SortOrder &order = SortOrder::ASC);
typedef std::function<void(T)> SingleRowCallback;
typedef std::function<void(std::vector<T>)> MultipleRowsCallback;
typedef std::function<void(const sise_t)> CountCallback;
Mapper(const DbClient &client) : _client(client) {}
T findByPrimaryKey(const T::PrimaryKeyType &key) noexcept(false);
@ -38,18 +51,177 @@ class Mapper
const ExceptionCallback &ecb) noexcept;
std::future<std::vector<T>> findFutureAll() noexcept;
//find one
//find by conditions
//insert
//update
//update all
//count
//limit/offset/orderby
//...
size_t count(const Criteria &criteria) noexcept(false);
void count(const Criteria &criteria,
const CountCallback &rcb,
const ExceptionCallback &ecb) noexcept;
std::future<size_t> count(const Criteria &criteria) noexcept;
T findOne(const Criteria &criteria) noexcept(false);
void findOne(const Criteria &criteria,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept;
std::future<T> findFutureOne(const Criteria &criteria) noexcept;
std::vector<T> findBy(const Criteria &criteria) noexcept(false);
void findBy(const Criteria &criteria,
const MultipleRowsCallback &rcb,
const ExceptionCallback &ecb) noexcept;
std::future<std::vector<T>> findFutureBy(const Criteria &criteria) noexcept;
void insert(T &obj) noexcept(false);
void insert(const T &obj,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept;
std::future<T> insert(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;
private:
Dbclient &_client;
std::string getSqlForFindingByPrimaryKey();
};
template <typename T>
inline T Mapper<T>::findByPrimaryKey(const 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,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept
{
static_assert(T::hasPrimaryKey, "No primary key in the table!");
findOne(Criteria(T::primaryKeyName, key), rcb, ecb);
}
template <typename T>
inline std::future<T> Mapper<T>::findFutureByPrimaryKey(const T::PrimaryKeyType &key) noexcept
{
static_assert(T::hasPrimaryKey, "No primary key in the table!");
return findFutureOne(Criteria(T::primaryKeyName, key));
}
template <typename T>
inline T Mapper<T>::findOne(const Criteria &criteria) noexcept(false)
{
std::string sql = "select * from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
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;
}
if (r.size() == 0)
{
throw Failure("No records found that meet the criteria");
}
else if (r.size() > 1)
{
throw Failure("More than one record found");
}
auto row = r[0];
return T(row);
}
template <typename T>
inline void Mapper<T>::findOne(const Criteria &criteria,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept
{
std::string sql = "select * from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
auto binder = _client << sql;
if (criteria)
criteria.outputArgs(binder);
binder >> [=](const Result &r) {
if (r.size() == 0)
{
ecb(Failure("No records found that meet the criteria"));
}
else if (r.size() > 1)
{
ech(Failure("More than one record found"));
}
else
{
rcb(T(r[0]));
}
};
binder >> ecb;
}
template <typename T>
inline std::future<T> Mapper<T>::findFutureOne(const Criteria &criteria) noexcept
{
std::string sql = "select * from ";
sql += T::tableName;
if (criteria)
{
sql += " where ";
sql += criteria.criteriaString();
sql = _client.replaceSqlPlaceHolder(sql, "$?");
}
auto binder = _client << sql;
if (criteria)
criteria.outputArgs(binder);
std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>();
binder >> [=](const Result &r) {
if (r.size() == 0)
{
try
{
throw Failure("No records found that meet the criteria");
}
catch (...)
{
prom->set_exception(std::current_exception());
}
}
else if (r.size() > 1)
{
try
{
throw Failure("More than one record found");
}
catch (...)
{
prom->set_exception(std::current_exception());
}
}
else
{
prom->set_value(T(r[0]));
}
};
binder >> [=](const std::exception_ptr &e) {
prom->set_exception(e);
};
binder.exec();
return prom->get_future();
}
} // namespace orm
} // namespace drogon

View File

@ -23,6 +23,7 @@ class PgClient : public DbClient
{
public:
PgClient(const std::string &connInfo, const size_t connNum);
virtual std::string replaceSqlPlaceHolder(const std::string &sqlStr, const std::string &holderStr) const override;
private:
virtual void execSql(const std::string &sql,

View File

@ -28,22 +28,12 @@ void PgClient::execSql(const std::string &sql,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback)
{
// std::thread _thread([=]() {
// try
// {
// throw Failure("exception test!!!");
// }
// catch (...)
// {
// exceptCallback(std::current_exception());
// }
// });
// _thread.detach();
// rcb(Result());
_clientPtr->execSql(sql,paraNum,parameters,length,format,rcb,exceptCallback);
}
PgClient::PgClient(const std::string &connInfo, const size_t connNum):
_clientPtr(new PgClientImpl(connInfo,connNum))
std::string PgClient::replaceSqlPlaceHolder(const std::string &sqlStr, const std::string &holderStr) const {
return _clientPtr->replaceSqlPlaceHolder(sqlStr, holderStr);
}
PgClient::PgClient(const std::string &connInfo, const size_t connNum) : _clientPtr(new PgClientImpl(connInfo, connNum))
{
}

View File

@ -16,6 +16,8 @@
#include <memory>
#include <stdio.h>
#include <unistd.h>
#include <sstream>
using namespace drogon::orm;
DbConnectionPtr PgClientImpl::newConnection(trantor::EventLoop *loop)
@ -34,7 +36,7 @@ DbConnectionPtr PgClientImpl::newConnection(trantor::EventLoop *loop)
//std::cout<<"Conn closed!end"<<std::endl;
});
connPtr->setOkCallback([=](const DbConnectionPtr &okConnPtr) {
LOG_TRACE << "postgreSQL connected!" ;
LOG_TRACE << "postgreSQL connected!";
std::lock_guard<std::mutex> guard(_connectionsMutex);
_readyConnections.insert(okConnPtr);
});
@ -151,7 +153,7 @@ void PgClientImpl::execSql(const std::string &sql,
{
throw BrokenConnection("No connection to postgreSQL server");
}
catch(...)
catch (...)
{
exceptCb(std::current_exception());
}
@ -200,3 +202,24 @@ void PgClientImpl::execSql(const std::string &sql,
_sqlCmdBuffer.push_back(std::move(cmd));
}
}
std::string PgClientImpl::replaceSqlPlaceHolder(const std::string &sqlStr, const std::string &holderStr) const
{
std::string::size_type startPos = 0;
std::string::size_type pos;
std::stringstream ret;
size_t phCount = 1;
do
{
pos = sqlStr.find(holderStr, startPos);
if (pos == std::string::npos)
{
ret << sqlStr.substr(startPos);
return ret.str();
}
ret << sqlStr.substr(startPos, pos - startPos);
ret << "$";
ret << phCount++;
startPos = pos + holderStr.length();
} while (1);
}

View File

@ -29,6 +29,7 @@ class PgClientImpl : public trantor::NonCopyable
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback);
std::string replaceSqlPlaceHolder(const std::string &sqlStr, const std::string &holderStr) const;
private:
void ioLoop();