最近工作中需要将一些信息保存到数据库,由于使用的是C++,没有反射机制,所以对应的ORM库非常少,手写sql又非常麻烦,这个时候非常羡慕java,C#等语言可以在编译的时候就将字段信息等编译到可执行文件中,因此可以非常轻松的实现反射,也有非常多好用成熟的ORM库。
本文主要是利用C++的宏和模板来实现非侵入式简单的C++ORM的功能(主要是针对sqlite数据库)
(1)要利用C++实现反射,最主要的是可以获取C++结构体各个字段的字段名,字段类型。我在网上找到一段如下的宏可以很好的解决这个问题。
#define DEFINE_STRUCT_SCHEMA(Struct, ...) \
template <> \
inline auto StructSchema<Struct>() \
{ \
using _Struct = Struct; \
return std::make_tuple(__VA_ARGS__); \
} \
template<> \
inline std::string GetTableName<Struct>() \
{ \
return #Struct; \
}
#define DEFINE_STRUCT_FIELD(StructField) \
std::make_tuple( \
(int)(&(((_Struct*)0 )->StructField)), \
#StructField, \
&_Struct::StructField)
这个宏示例如下:
struct SimpleStruct
{
int m_id;
int m_iAge;
double m_dHeight;
std::string m_strName;
};
DEFINE_STRUCT_SCHEMA
(
SimpleStruct,
DEFINE_STRUCT_FIELD(m_id),
DEFINE_STRUCT_FIELD(m_iAge),
DEFINE_STRUCT_FIELD(m_dHeight),
DEFINE_STRUCT_FIELD(m_strName)
);
我们可以发现这个是是将各个字段的信息(各个成员变量的的地址偏移,字段名,成员指针)保存在tuple之中,因为结构体的每个成员变量的地址都是不同的数据类型,用tuple去保存是一个合理的选择。
(2)由于信息都是保存在tuple之中,我们还需要提供遍历各个字段信息的方式。代码如下:
namespace detail
{
template <typename Fn, typename Tuple, std::size_t... I>
inline void ForEachTuple(Tuple&& tuple,Fn&& fn,std::index_sequence<I...>)
{
using Expander = int[];
(void)Expander {0, ((void)fn(I,std::get<I>(std::forward<Tuple>(tuple))), 0)...};
}
template <typename Fn, typename Tuple>
inline void ForEachTuple(Tuple&& tuple, Fn&& fn)
{
ForEachTuple(std::forward<Tuple>(tuple), std::forward<Fn>(fn),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{});
}
} // namespace detail
其实本质就是对可变参数模板的展开。然后在展开的过程中对每个字段信息调用Fn函数进行处理。
(3)有了以上两个基础机制,我们现在可以很容易的获取各个字段的信息了。首先我们完成两个函数:
*获取各个字段的字段名,类型。我们将信息保存到一个vector中;
*通过成员变量地址获取指定字段信息
代码如下:
enum class ValueType
{
UNDEFINE = 0,
INTEGER = 1,
BOOL,
REAL,
TEXT,
BLOB
};
struct FieldInfo
{
public:
FieldInfo(const std::string& strFieldName, ValueType valueType, int offset) :
m_strFieldName(strFieldName),
m_valueType(valueType),
m_iOffset(offset)
{}
public:
std::string m_strFieldName;
ValueType m_valueType;
int m_iOffset;
};
//绑定浮点型
template <typename Entity, typename Field>
constexpr T_IF_REAL(Field, ValueType) GetFieldType(Field Entity::*)
{
return ValueType::REAL;
}
//绑定整型
template <typename Entity, typename Field>
constexpr T_IF_INT(Field,ValueType) GetFieldType(Field Entity::*)
{
return ValueType::INTEGER;
}
//绑定布尔型
template <typename Entity, typename Field>
constexpr T_IF_BOOL(Field, ValueType) GetFieldType(Field Entity::*)
{
return ValueType::BOOL;
}
//绑定字符串
template <typename Entity, typename Field>
constexpr T_IF_TEXT(Field, ValueType) GetFieldType(Field Entity::*)
{
return ValueType::TEXT;
}
//绑定浮点型
template <typename Entity, typename Field,typename Entity2,typename Field2>
typename std::enable_if<std::is_same<Field Entity::*, Field2 Entity2::*>::value,bool>::type
CmpFieldAdd(Field Entity::*p1,Field2 Entity2::* p2, ValueType& valueType)
{
if (p1 == p2)
{
valueType = GetFieldType(p2);
return true;
}
return false;
}
template <typename Entity, typename Field, typename Entity2, typename Field2>
constexpr typename std::enable_if<!std::is_same<Field Entity::*, Field2 Entity2::*>::value, bool>::type
CmpFieldAdd(Field Entity::*p1, Field2 Entity2::* p2,ValueType& valueType)
{
return false;
}
template <typename Entity>
inline std::vector<FieldInfo> GetStructFieldInfos()
{
auto struct_schema = StructSchema<std::decay_t<Entity>>();
std::vector<FieldInfo> vecFieldInfo;
detail::ForEachTuple(struct_schema, [&](size_t index, auto&& field_schema)
{
using FieldSchema = std::decay_t<decltype(field_schema)>;
int offset = std::get<0>(std::forward<decltype(field_schema)>(field_schema));
std::string fieldName = std::get<1>(std::forward<decltype(field_schema)>(field_schema));
ValueType valueType = GetFieldType(std::get<2>(std::forward<decltype(field_schema)>(field_schema)));
vecFieldInfo.emplace_back(fieldName, valueType, offset);
});
return vecFieldInfo;
}
template<typename Entity,typename Field>
inline FieldInfo GetStructFieldInfo(Field Entity::* pp)
{
auto struct_schema = StructSchema<std::decay_t<Entity>>();
FieldInfo fieldInfo("", ValueType::UNDEFINE,0);
detail::ForEachTuple(struct_schema, [&](size_t index, auto&& field_schema)
{
using FieldSchema = std::decay_t<decltype(field_schema)>;
auto fieldPtr = std::get<2>(std::forward<decltype(field_schema)>(field_schema));
ValueType valueType = ValueType::UNDEFINE;
if (CmpFieldAdd(pp, fieldPtr, valueType))
{
fieldInfo.m_iOffset = std::get<0>(std::forward<decltype(field_schema)>(field_schema));
fieldInfo.m_strFieldName = std::get<1>(std::forward<decltype(field_schema)>(field_schema));
fieldInfo.m_valueType = valueType;
}
});
return fieldInfo;
}
我将结构体字段分为了几大类,与数据库里字段对应。
以上代码用到了类似T_IF_INT这种宏,定义如下:
#define T_IF_INT(Field,T) \
typename std::enable_if< \
(std::is_enum<std::decay_t<Field>>::value \
|| std::is_integral<std::decay_t<Field>>::value) && \
!std::is_same<std::decay_t<Field>, long long>::value && \
!std::is_same<std::decay_t<Field>, unsigned long>::value && \
!std::is_same<std::decay_t<Field>, bool>::value && \
!std::is_same<std::decay_t<Field>, char>::value && \
!std::is_same<std::decay_t<Field>, wchar_t>::value && \
!std::is_same<std::decay_t<Field>, char16_t>::value && \
!std::is_same<std::decay_t<Field>, char32_t>::value && \
!std::is_same<std::decay_t<Field>, unsigned char>::value, \
T>::type
#define T_IF_REAL(Field,T) \
typename std::enable_if<std::is_floating_point<std::decay_t<Field>>::value, T>::type
#define T_IF_BOOL(Field,T) \
typename std::enable_if<std::is_same<std::decay_t<Field>,bool>::value, T>::type
#define T_IF_TEXT(Field,T) \
typename std::enable_if<std::is_same<std::decay_t<Field>, std::string>::value, T>::type
这些宏主要是采用了SFINAE机制,这些宏在下面的绑定参数和设置各个成员的值也有用到,所以定义成了宏。
(4)有了以上函数,我们首先可以完成创建数据库表的功能。
但是创建数据库表还需要对字段有约束。因此定义一个结构体表达字段的约束信息,如下:
#define PK 0x0001
#define NOT_NULL 0x0010
#define UNQUE 0x0100
struct FieldContraint
{
public:
template<typename Entity,typename Field>
FieldContraint(Field Entity::* pp, int contraintType)
{
FieldInfo fieldInfo = GetStructFieldInfo(pp);
m_strFieldName = fieldInfo.m_strFieldName;
if (contraintType&PK)
{
m_strContraint.append(" PRIMARY KEY");
}
if (contraintType&NOT_NULL)
{
m_strContraint.append(" NOT NULL");
}
if (contraintType&UNQUE)
{
m_strContraint.append(" UNIQUE");
}
}
std::string m_strFieldName;
std::string m_strContraint;
};
目前只做了主键,唯一约束,非空约束。
有了以上信息就可以生成完整的create sql语句了。
(2)插入,更新sql语句我们采用预编译来完成,其中有两部分:一个是预编译语句,这个用以上涉及的函数就可以完成,还有一个是参数绑定。对于查询语句,主要是将数据库各个列的值赋值给结构体,因此成为字段的绑定与获取,主要代码如下:
template <typename Entity, typename Fn>
inline void TravFieldValue(sqlite3* pDb, sqlite3_stmt* m_statement, Entity&& entity, Fn&& fn)
{
auto struct_schema = StructSchema<std::decay_t<Entity>>();
detail::ForEachTuple(struct_schema, [&](size_t index, auto&& field_schema)
{
using FieldSchema = std::decay_t<decltype(field_schema)>;
fn(pDb,m_statement, entity.*std::get<2>(std::forward<decltype(field_schema)>(field_schema)),
(int)index);
});
}
struct SetFieldValueFn
{
void CheckColumnDataType(sqlite3_stmt* m_statement, int index, int idataType)
{
int coltype = sqlite3_column_type(m_statement, index);
if(coltype != idataType)
throw std::runtime_error("date type is not match");
}
//获取浮点型成员变量
template <typename Field>
T_IF_REAL(Field, void)
SetFieldValue(sqlite3* pDb, sqlite3_stmt* m_statement, Field&& fieldValue, int index)
{
CheckColumnDataType(m_statement,index,SQLITE_FLOAT);
fieldValue = (std::decay_t<Field>)sqlite3_column_double(m_statement, index);
}
//获取整型成员变量
template <typename Field>
T_IF_INT(Field, void)
SetFieldValue(sqlite3* pDb, sqlite3_stmt* m_statement, Field&& fieldValue, int index)
{
CheckColumnDataType(m_statement, index, SQLITE_INTEGER);
fieldValue = (std::decay_t<Field>)sqlite3_column_int(m_statement, index);
}
//获取布尔型成员变量
template <typename Field>
T_IF_BOOL(Field, void)
SetFieldValue(sqlite3* pDb, sqlite3_stmt* m_statement, Field&& fieldValue, int index)
{
CheckColumnDataType(m_statement, index, SQLITE_INTEGER);
fieldValue = (std::decay_t<Field>)sqlite3_column_int(m_statement, index);
}
//获取字符串成员变量
template <typename Field>
T_IF_TEXT(Field, void)
SetFieldValue(sqlite3* pDb, sqlite3_stmt* m_statement, Field&& fieldValue, int index)
{
CheckColumnDataType(m_statement, index, SQLITE_TEXT);
const char* valuePtr = (const char*)sqlite3_column_text(m_statement, index);
ConvertUtf8StringToString(valuePtr,fieldValue);
}
template<typename Field> void
operator()(sqlite3* pDb, sqlite3_stmt* m_statement, Field&& fieldValue, int index)
{
SetFieldValue(pDb, m_statement, fieldValue, index);
}
};
template<typename Entity>
void SetFieldVal(sqlite3* pDb, sqlite3_stmt* m_statement, Entity&& entity)
{
SetFieldValueFn setFieldValueFn;
TravFieldValue(pDb, m_statement, entity, setFieldValueFn);
}
struct BindFieldValueFn
{
//绑定浮点型
template <typename Field>
T_IF_REAL(Field, void)
BindParam(sqlite3* pDb, sqlite3_stmt* m_statement, Field&& fieldValue, int index)
{
if (sqlite3_bind_double(m_statement, index + 1, (double)fieldValue) != SQLITE_OK)
throw std::runtime_error(std::string("SQL error:bind param failed!'") + sqlite3_errmsg(pDb));
}
//绑定整型
template <typename Field>
T_IF_INT(Field, void)
BindParam(sqlite3* pDb, sqlite3_stmt* m_statement, Field&& fieldValue, int index)
{
if (sqlite3_bind_int(m_statement, index + 1, (int)fieldValue) != SQLITE_OK)
throw std::runtime_error(std::string("SQL error:bind param failed!'") + sqlite3_errmsg(pDb));
}
//绑定布尔型
template <typename Field>
T_IF_BOOL(Field, void)
BindParam(sqlite3* pDb, sqlite3_stmt* m_statement, Field&& fieldValue, int index)
{
if (sqlite3_bind_int(m_statement, index + 1, fieldValue ? 1 : 0) != SQLITE_OK)
throw std::runtime_error(std::string("SQL error:bind param failed!'") + sqlite3_errmsg(pDb));
}
//绑定字符串
template <typename Field>
T_IF_TEXT(Field, void)
BindParam(sqlite3* pDb, sqlite3_stmt* m_statement, Field&& fieldValue, int index)
{
std::string strUtf8Str;
ConvertStringToUTF8String(fieldValue,strUtf8Str);
if (sqlite3_bind_text(m_statement, index + 1, strUtf8Str.c_str(), -1, SQLITE_TRANSIENT))
throw std::runtime_error(std::string("SQL error:bind param failed!'") + sqlite3_errmsg(pDb));
}
template<typename Field> void
operator()(sqlite3* pDb, sqlite3_stmt* m_statement, Field&& fieldValue, int index)
{
BindParam(pDb,m_statement,fieldValue,index);
}
};
template<typename Entity>
void BindFieldVal(sqlite3* pDb, sqlite3_stmt* m_statement, Entity&& entity)
{
BindFieldValueFn bindFieldFn;
TravFieldValue(pDb, m_statement, entity, bindFieldFn);
}
(5)对于查询信息还需要有查询条件,我们也用一个结构体进行表达,如下:
enum class CondtionType
{
GREATER,
LESS,
EQ,
LIKE,
NOT_IN,
IN,
};
struct Condition
{
public:
template<class Entity,class Field>
Condition(Field Entity::* fieldPtr, CondtionType conType, const std::string& value)
{
static FieldInfo fieldInfo = GetStructFieldInfo(fieldPtr);
if (fieldInfo.m_valueType == ValueType::TEXT)
{
strCondition = fieldInfo.m_strFieldName + GetStrByConType(conType) +"'"
+ value +"'";
}
else
{
strCondition = fieldInfo.m_strFieldName + GetStrByConType(conType) + value;
}
}
Condition operator &&(const Condition& con)
{
return Condition(strCondition + " AND " + con.strCondition);
}
Condition operator||(const Condition& con)
{
return Condition(strCondition + " OR " + con.strCondition);
}
std::string strCondition;
private:
Condition(const std::string& str) :strCondition(str) {}
std::string GetStrByConType(CondtionType conType)
{
switch (conType)
{
case CondtionType::GREATER:
return " > ";
case CondtionType::LESS:
return " < ";
case CondtionType::EQ:
return " = ";
case CondtionType::LIKE:
return " like ";
case CondtionType::NOT_IN:
return " NOT IN ";
case CondtionType::IN:
return " IN ";
default:
return " ";
}
return " ";
}
};
目前仅仅支持了大于,小于,等于,like等。
(5)基础函数都完成了,接下来完成SqliteDb类,如下:
class SqliteDB
{
public:
SqliteDB()
{
}
~SqliteDB()
{
close();
}
void Open(const std::string& fileName)
{
std::string utf8FileName;
ConvertStringToUTF8String(fileName, utf8FileName);
if (sqlite3_open(utf8FileName.c_str(), &db) != SQLITE_OK)
{
close();
throw std::runtime_error(std::string("SQL error: Can't open database '") + sqlite3_errmsg(db) + "'");
}
m_statement = nullptr;
}
void BeginTrans()
{
Execute("begin transaction;");
}
void Commit()
{
Execute("commit transaction;");
}
template<class T>
void CreateTableIfNotExist(const std::vector<FieldContraint>& vecFieldContraint)
{
std::string&& strUtf8CreateSql = GenUtf8CreateSql<std::decay_t<T>>(vecFieldContraint);
Execute(strUtf8CreateSql);
}
template<class T>
void Insert(T&& t)
{
static std::string&& strUtf8PreInsertSql = GenUtf8InsertSql<std::decay_t<T>>();
Prepare(strUtf8PreInsertSql);
BindFieldVal(db,m_statement,t);
ExecuteParparseSQL();
}
template<typename T, typename Out>
void Quary(Out& out)
{
static std::string&& strUtf8SelectSql = GenUtf8SelectSql<std::decay_t<T>>();
Prepare(strUtf8SelectSql);
GetT<T,Out>(out);
}
template<typename T,typename Out>
void Quary(const Condition& condi, Out& out)
{
std::string&& strUtf8SelectSql = GenUtf8SelectSql<std::decay_t<T>>(condi);
Prepare(strUtf8SelectSql);
GetT<T, Out>(out);
}
template<typename T>
void Delete(const Condition& condi)
{
std::string strUtf8DelSql = GenUtf8DeleteSql<T>(condi);
Execute(strUtf8DelSql);
}
template<typename Entity,typename Field>
void Update(Entity&& t,Field std::decay_t<Entity>::* p)
{
static std::string&& strUtf8UpdatePreSql = GenUtf8UpdatePreSql(t,p);
Prepare(strUtf8UpdatePreSql);
BindFieldVal(db, m_statement, t);
ExecuteParparseSQL();
}
void Prepare(const std::string& sql)
{
m_code = sqlite3_prepare_v2(db, sql.data(), -1, &m_statement, nullptr);
if (m_code != SQLITE_OK)
{
throw std::runtime_error(std::string("SQL error: Prepare failed! '") + sqlite3_errmsg(db) + "'");
}
}
void ExecuteParparseSQL()
{
m_code = sqlite3_step(m_statement);
sqlite3_reset(m_statement);
if (m_code != SQLITE_DONE)
{
throw std::runtime_error(
std::string("SQL error: execute PrepareSQL failed! '") + sqlite3_errmsg(db) + "'");
}
}
void Execute(const std::string& cmd)
{
char* zErrMsg = NULL;
int rc = SQLITE_OK;
for (size_t iTry = 0; iTry < MAX_TRIAL; iTry++)
{
rc = sqlite3_exec(db, cmd.c_str(), 0, 0, &zErrMsg);
if (rc != SQLITE_BUSY)
break;
std::this_thread::sleep_for(std::chrono::microseconds(20));
}
if (rc != SQLITE_OK)
{
auto errStr = std::string("SQL error: '") + zErrMsg + "' at '" + cmd + "'";
sqlite3_free(zErrMsg);
throw std::runtime_error(errStr);
}
}
int GetColumnIntValue(int iColumnIdx)
{
CheckColumnValueType(iColumnIdx, SQLITE_INTEGER);
return sqlite3_column_int(m_statement,iColumnIdx);
}
double GetColumnDoubleValue(int iColumnIdx)
{
CheckColumnValueType(iColumnIdx, SQLITE_FLOAT);
return sqlite3_column_double(m_statement, iColumnIdx);
}
const char* GetColumnText(int iColumnIdx)
{
CheckColumnValueType(iColumnIdx, SQLITE_TEXT);
return (const char*)sqlite3_column_text(m_statement, iColumnIdx);
}
private:
template<typename T>
std::string GenUtf8CreateSql(const std::vector<FieldContraint>& vecFieldContraint)
{
std::stringstream os;
static std::vector<FieldInfo> vecFileInfo = GetStructFieldInfos<std::decay_t<T>>();
static std::string strTableName = GetTableName<std::decay_t<T>>();
os << "CREATE TABLE IF NOT EXISTS "<<strTableName<<"(";
for (int i = 0; i < (int)vecFileInfo.size(); i++)
{
auto& fieldInfo = vecFileInfo.at(i);
auto iter = std::find_if(vecFieldContraint.begin(),vecFieldContraint.end(),
[&](const FieldContraint& fieldContraint)
{
if(fieldContraint.m_strFieldName == fieldInfo.m_strFieldName)
return true;
return false;
});
std::string strContraint;
if (iter != vecFieldContraint.end())
{
strContraint.append(iter->m_strContraint);
}
strContraint.append(",");
os << fieldInfo.m_strFieldName;
if (fieldInfo.m_valueType == ValueType::INTEGER || fieldInfo.m_valueType == ValueType::BOOL)
{
os << " INT"<< strContraint;
}
else if (fieldInfo.m_valueType == ValueType::REAL)
{
os << " REAL" << strContraint;
}
else if (fieldInfo.m_valueType == ValueType::TEXT)
{
os <<" TEXT" << strContraint;
}
}
os.seekp(os.tellp() - std::streamoff(1));
os << ")";
std::string&& strCreateSql = os.str();
std::string strUtf8CreateSql;
ConvertStringToUTF8String(strCreateSql, strUtf8CreateSql);
return strUtf8CreateSql;
}
template<typename T>
std::string GenUtf8InsertSql()
{
std::stringstream os;
std::stringstream osVal;
static std::vector<FieldInfo> vecFileInfo = GetStructFieldInfos<std::decay_t<T>>();
static std::string strTableName = GetTableName<std::decay_t<T>>();
os<<"INSERT INTO " <<strTableName << " (";
for (int i = 0; i < (int)vecFileInfo.size(); i++)
{
os << vecFileInfo.at(i).m_strFieldName<<",";
osVal<<"?,";
}
os.seekp(os.tellp() - std::streamoff(1));
osVal.seekp(osVal.tellp() - std::streamoff(1));
osVal << ");"; // To Enable Seekp...
os << ") VALUES (" << osVal.str();
std::string&& strInsertSql = os.str();
std::string strUtf8InsertSql;
ConvertStringToUTF8String(strInsertSql, strUtf8InsertSql);
return strUtf8InsertSql;
}
template<typename T>
std::string GenUtf8SelectSql()
{
std::stringstream os;
static std::vector<FieldInfo> vecFileInfo = GetStructFieldInfos<std::decay_t<T>>();
static std::string strTableName = GetTableName<std::decay_t<T>>();
os << "SELECT " ;
for (int i = 0; i < (int)vecFileInfo.size(); i++)
{
os << vecFileInfo.at(i).m_strFieldName << ",";
}
os.seekp(os.tellp() - std::streamoff(1));
os << " FROM "<<strTableName;
std::string&& strSelectSql = os.str();
std::string strUtf8SelectSql;
ConvertStringToUTF8String(strSelectSql, strUtf8SelectSql);
return strUtf8SelectSql;
}
template<typename T>
std::string GenUtf8SelectSql(const Condition& codin)
{
std::string strWhere(" Where ");
strWhere.append(codin.strCondition);
std::string strUtf8Where;
ConvertStringToUTF8String(strWhere, strUtf8Where);
std::string strSelectSql = GenUtf8SelectSql<T>();
return strSelectSql.append(strUtf8Where);
}
template<typename T>
std::string GenUtf8DeleteSql(const Condition& codin)
{
std::string strDelSql = "Delete FROM ";
strDelSql.append(GetTableName<std::decay_t<T>>());
strDelSql.append(" WHERE ");
strDelSql.append(codin.strCondition);
std::string strUtf8DelSql;
ConvertStringToUTF8String(strDelSql, strUtf8DelSql);
return strUtf8DelSql;
}
template<typename T,typename Field>
std::string GenUtf8UpdatePreSql(T&& t, Field std::decay_t<T>::* pField)
{
std::stringstream os;
os << "UPDATE ";
os << GetTableName <std::decay_t<T>>();
os << " SET ";
static std::vector<FieldInfo> vecFileInfo = GetStructFieldInfos<std::decay_t<T>>();
for (int i = 0; i < (int)vecFileInfo.size(); i++)
{
FieldInfo& fieldInfo = vecFileInfo.at(i);
os << fieldInfo.m_strFieldName;
os << "=?,";
}
os.seekp(os.tellp() - std::streamoff(1));
os << " WHERE ";
FieldInfo fieldInfo = GetStructFieldInfo(pField);
os << fieldInfo.m_strFieldName<<"=";
auto& fieldValue = t.*pField;
os << fieldValue <<";";
std::string strUtf8UpdateSql;
ConvertStringToUTF8String(os.str(), strUtf8UpdateSql);
return strUtf8UpdateSql;
}
template<typename T,typename Out>
void GetT(Out& out)
{
do
{
int r = sqlite3_step(m_statement);
if (r == SQLITE_DONE)
break;
if (r != SQLITE_ROW)
break;
T entity;
out.push_back(entity);
auto it = out.end();
it--;
T& entityR = *it;
SetFieldVal(db, m_statement, entityR);
} while (true);
}
private:
void CheckColumnValueType(int iColumnIdx, int iDataType)
{
int coltype = sqlite3_column_type(m_statement, iColumnIdx);
if (coltype != iDataType)
throw std::runtime_error("date type is not match:wstring");
}
int CloseDBHandle()
{
int code = sqlite3_close(db);
while (code == SQLITE_BUSY)
{
code = SQLITE_OK;
sqlite3_stmt* stmt = sqlite3_next_stmt(db,NULL);
if(stmt == nullptr)
break;
code = sqlite3_finalize(stmt);
if(code == SQLITE_OK)
code = sqlite3_close(db);
}
return code;
}
bool close()
{
if(db == nullptr)
return true;
if(m_statement != nullptr)
sqlite3_finalize(m_statement);
m_code = CloseDBHandle();
bool ret = (SQLITE_OK == m_code);
m_statement = nullptr;
db = nullptr;
return ret;
}
private:
SqliteDB(const SqliteDB&) = delete;
SqliteDB& operator=(const SqliteDB&) = delete;
private:
sqlite3* db = nullptr;
const static size_t MAX_TRIAL = 16;
sqlite3_stmt* m_statement = nullptr;
int m_code;
};
(6)由于sqlite相关函数只接收utf8格式的字符串,因此还需要提供几个转换函数:
void ConvertWstringToUTF8String(const std::wstring& wstr, std::string& str)
{
int len = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), -1, nullptr, 0, nullptr, nullptr);
char* strPtr = new char[len + 1];
memset(strPtr, 0, len + 1);
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), -1, strPtr, len, nullptr, nullptr);
str = strPtr;
delete[] strPtr;
}
//将string转换为wstring
void ConvertUTF8StringToWstring(std::string& str, std::wstring wstr)
{
//将UTF-8转换为wstring
int len = MultiByteToWideChar(CP_UTF8, NULL, str.data(), str.size(), NULL, 0);
wchar_t* wszString = new wchar_t[len + 1];
MultiByteToWideChar(CP_UTF8, NULL, str.data(), str.size(), wszString, len);
wszString[len] = L'\0';
wstr = wszString;
delete[] wszString;
}
void ConvertUTF8StringToWstring(const char*& strPtr, std::wstring& wstr)
{
//将UTF-8转换为wstring
int len = MultiByteToWideChar(CP_UTF8, NULL, strPtr, -1, NULL, 0);
wchar_t* wszString = new wchar_t[len + 1];
MultiByteToWideChar(CP_UTF8, NULL, strPtr, -1, wszString, len);
wszString[len] = L'\0';
wstr = wszString;
delete[] wszString;
}
void ConvertStringToUTF8String(const std::string& str, std::string& utf8Str)
{
int nwLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
wchar_t* pwBuf = new wchar_t[nwLen + 1];//一定要加1,不然会出现尾巴
ZeroMemory(pwBuf, nwLen * 2 + 2);
::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), pwBuf, nwLen);
int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
char* pBuf = new char[nLen + 1];
ZeroMemory(pBuf, nLen + 1);
::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
utf8Str = pBuf;
delete[]pwBuf;
delete[]pBuf;
pwBuf = NULL;
pBuf = NULL;
}
void ConvertUtf8StringToString(const std::string& utf8Str, std::string& str)
{
int nwLen = ::MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, NULL, 0);
wchar_t* pwBuf = new wchar_t[nwLen + 1];//一定要加1,不然会出现尾巴
ZeroMemory(pwBuf, nwLen * 2 + 2);
::MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), utf8Str.length(), pwBuf, nwLen);
int nLen = ::WideCharToMultiByte(CP_ACP, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
char* pBuf = new char[nLen + 1];
ZeroMemory(pBuf, nLen + 1);
::WideCharToMultiByte(CP_ACP, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
str = pBuf;
delete[]pwBuf;
delete[]pBuf;
pwBuf = NULL;
pBuf = NULL;
}
(7)至此,一个简单的非侵入的C++ ORM就完成了,看下测试代码:
int main()
{
std::string strDbPath = "D:\\test.db";
SqliteDB db;
db.Open(strDbPath);
db.BeginTrans();
FieldContraint fielContraint(&SimpleStruct::m_id,PK|NOT_NULL| UNQUE);
db.CreateTableIfNotExist<SimpleStruct>({ fielContraint });
for (int i = 0; i < 100; i++)
{
SimpleStruct ss1 = { i,i,(double)i,"哈哈"};
db.Insert(ss1);
}
Condition condit1 = {&SimpleStruct::m_iAge,CondtionType::GREATER,"10"};
Condition condit2 = { &SimpleStruct::m_strName,CondtionType::LIKE,"%ABC%" };
std::vector<SimpleStruct> vecSimpleStruct;
db.Quary<SimpleStruct>(condit1,vecSimpleStruct);
db.Delete<SimpleStruct>({ &SimpleStruct::m_id,CondtionType::GREATER,"10" });
SimpleStruct ss = { 0,1,(double)1,"哈哈" };
db.Update(ss,&SimpleStruct::m_id);
db.Commit();
std::cout<<"成功"<<std::endl;
system("pause");
return 0;
}