From 8bd31016de92222c618848f421bb9bbf059d7248 Mon Sep 17 00:00:00 2001 From: ubuntu14 Date: Wed, 19 Aug 2015 05:10:52 -0700 Subject: [PATCH] add transaction API in db_handle and db_mysql --- lib_acl_cpp/changes.txt | 3 ++ lib_acl_cpp/include/acl_cpp/db/db_handle.hpp | 48 +++++++++++++------ lib_acl_cpp/include/acl_cpp/db/db_mysql.hpp | 15 +++++- lib_acl_cpp/samples/db/mysql_manager/main.cpp | 2 +- lib_acl_cpp/samples/db/mysql_pool/main.cpp | 2 +- lib_acl_cpp/samples/db/mysql_query/main.cpp | 16 ++++++- lib_acl_cpp/src/db/db_mysql.cpp | 41 +++++++++++++++- lib_acl_cpp/src/db/db_sqlite.cpp | 11 ++++- lib_acl_cpp/src/http/HttpServletResponse.cpp | 4 +- 9 files changed, 116 insertions(+), 26 deletions(-) diff --git a/lib_acl_cpp/changes.txt b/lib_acl_cpp/changes.txt index d4fb4ccdf..b0544e401 100644 --- a/lib_acl_cpp/changes.txt +++ b/lib_acl_cpp/changes.txt @@ -1,6 +1,9 @@ 修改历史列表: ------------------------------------------------------------------------ +347) 2015.8.19 +347.1) feature: db_handle/db_mysql 支持事务提交接口 + 346) 2015.8.18 346.1) feature: http_client/http_request 类在读取压缩数据体时,会自动处理 临时处理结果为 0 的情况,以降低使用复杂度 diff --git a/lib_acl_cpp/include/acl_cpp/db/db_handle.hpp b/lib_acl_cpp/include/acl_cpp/db/db_handle.hpp index 80241bc3c..aa3b8151f 100644 --- a/lib_acl_cpp/include/acl_cpp/db/db_handle.hpp +++ b/lib_acl_cpp/include/acl_cpp/db/db_handle.hpp @@ -63,7 +63,7 @@ class ACL_CPP_API db_row * 从查询结果的记录行中取得对应下标的整数类型的字段值 * @param ifield {size_t} 下标值 * @param null_value {int} 当结果为空时,返回此值表示未有相应结果 - * @return {int} 当返回值与用户输入的 null_value 值相同时表明没有查到结果 + * @return {int} 当返回值与用户输入的 null_value 相同表明没有查到结果 */ int field_int(size_t ifield, int null_value = 0) const; @@ -71,7 +71,7 @@ class ACL_CPP_API db_row * 从查询结果的记录行中取得字段名的整数类型的字段值 * @param name {const char*} 下标值 * @param null_value {int} 当结果为空时,返回此值表示未有相应结果 - * @return {int} 当返回值与用户输入的 null_value 值相同时表明没有查到结果 + * @return {int} 当返回值与用户输入的 null_value 相同表明没有查到结果 */ int field_int(const char* name, int null_value = 0) const; @@ -80,31 +80,36 @@ class ACL_CPP_API db_row * 从查询结果的记录行中取得对应下标的整数类型的字段值 * @param ifield {size_t} 下标值 * @param null_value {acl_int64} 当结果为空时,返回此值表示未有相应结果 - * @return {acl_int64} 当返回值与用户输入的 null_value 值相同时表明没有查到结果 + * @return {acl_int64} 当返回值与用户输入的 null_value 值相同时表明 + * 没有查到结果 */ #if defined(_WIN32) || defined(_WIN64) __int64 field_int64(size_t ifield, __int64 null_value = 0) const; #else - long long int field_int64(size_t ifield, long long int null_value = 0) const; + long long int field_int64(size_t ifield, + long long int null_value = 0) const; #endif /** * 从查询结果的记录行中取得字段名的整数类型的字段值 * @param name {const char*} 下标值 * @param null_value {acl_int64} 当结果为空时,返回此值表示未有相应结果 - * @return {acl_int64} 当返回值与用户输入的 null_value 值相同时表明没有查到结果 + * @return {acl_int64} 当返回值与用户输入的 null_value 值相同时表明 + * 没有查到结果 */ #if defined(_WIN32) || defined(_WIN64) __int64 field_int64(const char* name, __int64 null_value = 0) const; #else - long long int field_int64(const char* name, long long int null_value = 0) const; + long long int field_int64(const char* name, + long long int null_value = 0) const; #endif /** * 从查询结果的记录行中取得字段名的浮点类型的字段值 * @param ifield {size_t} 下标值 * @param null_value {double} 当结果为空时,返回此值表示未有相应结果 - * @return {double} 当返回值与用户输入的 null_value 值相同时表明没有查到结果 + * @return {double} 当返回值与用户输入的 null_value 值相同时表明没有 + * 查到结果 */ double field_double(size_t ifield, double null_value = 0.0) const; @@ -112,7 +117,8 @@ class ACL_CPP_API db_row * 从查询结果的记录行中取得字段名的浮点类型的字段值 * @param name {const char*} 下标值 * @param null_value {double} 当结果为空时,返回此值表示未有相应结果 - * @return {double} 当返回值与用户输入的 null_value 值相同时表明没有查到结果 + * @return {double} 当返回值与用户输入的 null_value 值相同时表明没有 + * 查到结果 */ double field_double(const char* name, double null_value = 0.0) const; @@ -312,16 +318,28 @@ class ACL_CPP_API db_handle : public connect_client virtual bool sql_update(const char* sql) = 0; /** - * 更安全易用的查询过程,调用此函数功能等同于 sql_select,只是查询对象 query - * 构建的 sql 语句是安全的,可以防止 sql 注入 + * 开始执行事务 + * @return {bool} + */ + virtual bool begin_transaction() { return false; } + + /** + * 提交事务 + * @return {bool} + */ + virtual bool commit() { return false; } + + /** + * 更安全易用的查询过程,调用此函数功能等同于 sql_select,只是查询 + * 对象 query 构建的 sql 语句是安全的,可以防止 sql 注入 * @param query {query&} * @return {bool} 执行是否成功 */ bool exec_select(query& query); /** - * 更安全易用的更新过程,调用此函数功能等同于 sql_update,只是查询对象 query - * 构建的 sql 语句是安全的,可以防止 sql 注入 + * 更安全易用的更新过程,调用此函数功能等同于 sql_update,只是查询 + * 对象 query 构建的 sql 语句是安全的,可以防止 sql 注入 * @param query {query&} * @return {bool} 执行是否成功 */ @@ -372,9 +390,9 @@ class ACL_CPP_API db_handle : public connect_client const std::vector* get_rows() const; /** - * 获得执行 SQL 语句后的第一行结果,这对于唯一键的数据查询时显得比较便捷些 - * @return {const db_row*} 返回空表示查询结果为空,如果返回结果非空,则必须 - * 调用 free_result() 函数来释放中间的结果内存,否则会引起内存泄露 + * 获得执行 SQL 语句后的第一行结果,针对唯一键的数据查询比较方便 + * @return {const db_row*} 返回空表示查询结果为空,否则, 则必须调用 + * free_result() 函数来释放中间的结果内存,否则会引起内存泄露 */ const db_row* get_first_row() const; diff --git a/lib_acl_cpp/include/acl_cpp/db/db_mysql.hpp b/lib_acl_cpp/include/acl_cpp/db/db_mysql.hpp index 40681b811..3c556922d 100644 --- a/lib_acl_cpp/include/acl_cpp/db/db_mysql.hpp +++ b/lib_acl_cpp/include/acl_cpp/db/db_mysql.hpp @@ -38,7 +38,7 @@ class ACL_CPP_API db_mysql : public db_handle } /********************************************************************/ - /* 以下为基类 db_handle 的虚接口 */ + /* 以下为基类 db_handle 的虚接口 */ /********************************************************************/ /** @@ -106,6 +106,19 @@ class ACL_CPP_API db_mysql : public db_handle */ int affect_count() const; + /** + * 基类 db_handle 的虚函数,用来表示事务的开始,注意若要使用事务方式, + * 则需要在 db_mysql 的构造函数中传入的参数 auto_commit 为 false + * @return {bool} + */ + bool begin_transaction(); + + /** + * 基类 db_handle 的虚函数,用来表示事务的结束 + * @return {bool} + */ + bool commit(); + private: char* dbaddr_; // 数据库监听地址 char* dbname_; // 数据库名 diff --git a/lib_acl_cpp/samples/db/mysql_manager/main.cpp b/lib_acl_cpp/samples/db/mysql_manager/main.cpp index fb1661edb..f367af580 100644 --- a/lib_acl_cpp/samples/db/mysql_manager/main.cpp +++ b/lib_acl_cpp/samples/db/mysql_manager/main.cpp @@ -386,7 +386,7 @@ int main(void) acl::db_handle::set_loadpath(path); acl::string dbaddr("127.0.0.1:3306"); - acl::string dbname("acl_db"), dbuser("root"), dbpass("111111"); + acl::string dbname("acl_db"), dbuser("root"), dbpass; out.format("Enter dbaddr [default: %s]: ", dbaddr.c_str()); if (in.gets(line) && !line.empty()) diff --git a/lib_acl_cpp/samples/db/mysql_pool/main.cpp b/lib_acl_cpp/samples/db/mysql_pool/main.cpp index e185c2586..fc32cacfd 100644 --- a/lib_acl_cpp/samples/db/mysql_pool/main.cpp +++ b/lib_acl_cpp/samples/db/mysql_pool/main.cpp @@ -362,7 +362,7 @@ int main(void) acl::db_handle::set_loadpath(path); acl::string dbaddr("127.0.0.1:3306"); - acl::string dbname("acl_db"), dbuser("root"), dbpass("111111"); + acl::string dbname("acl_db"), dbuser("root"), dbpass; out.format("Enter dbaddr [default: %s]: ", dbaddr.c_str()); if (in.gets(line) && !line.empty()) diff --git a/lib_acl_cpp/samples/db/mysql_query/main.cpp b/lib_acl_cpp/samples/db/mysql_query/main.cpp index 7fbcc4959..a39407520 100644 --- a/lib_acl_cpp/samples/db/mysql_query/main.cpp +++ b/lib_acl_cpp/samples/db/mysql_query/main.cpp @@ -98,6 +98,12 @@ static bool tbl_create(const char* dbaddr, const char* dbname, // 添加表数据 static bool tbl_insert(acl::db_handle& db, int n) { + if (db.begin_transaction() == false) + { + printf("begin transaction false: %s\r\n", db.get_error()); + return false; + } + acl::query query; query.create_sql("insert into group_tbl(group_name, uvip_tbl," " update_date) values(:group, :test, :date)") @@ -108,6 +114,12 @@ static bool tbl_insert(acl::db_handle& db, int n) if (db.exec_update(query) == false) return (false); + if (db.commit() == false) + { + printf("commit error: %s\r\n", db.get_error()); + return false; + } + const acl::db_rows* result = db.get_result(); if (result) { @@ -263,7 +275,7 @@ int main(void) acl::db_handle::set_loadpath(path); acl::string dbaddr("127.0.0.1:3306"); - acl::string dbname("acl_db"), dbuser("root"), dbpass("111111"); + acl::string dbname("acl_db"), dbuser("root"), dbpass; out.format("Enter dbaddr [default: %s]: ", dbaddr.c_str()); if (in.gets(line) && !line.empty()) @@ -316,7 +328,7 @@ int main(void) ////////////////////////////////////////////////////////////////////// - acl::db_mysql db(dbaddr, dbname, dbuser, dbpass); + acl::db_mysql db(dbaddr, dbname, dbuser, dbpass, 0, false); int max = 100; // 先打开数据库连接 diff --git a/lib_acl_cpp/src/db/db_mysql.cpp b/lib_acl_cpp/src/db/db_mysql.cpp index 59e30dbf4..d9ec21db4 100644 --- a/lib_acl_cpp/src/db/db_mysql.cpp +++ b/lib_acl_cpp/src/db/db_mysql.cpp @@ -67,6 +67,8 @@ static mysql_character_set_name_fn __mysql_character_set_name = NULL; static acl_pthread_once_t __mysql_once = ACL_PTHREAD_ONCE_INIT; static ACL_DLL_HANDLE __mysql_dll = NULL; +static acl::string __mysql_path; + // 程序退出释放动态加载的库 static void __mysql_dll_unload(void) { @@ -74,7 +76,7 @@ static void __mysql_dll_unload(void) { acl_dlclose(__mysql_dll); __mysql_dll = NULL; - logger("libmysql.dll unload ok"); + logger("%s unload ok", __mysql_path.c_str()); } } @@ -82,7 +84,8 @@ static void __mysql_dll_unload(void) static void __mysql_dll_load(void) { if (__mysql_dll != NULL) - logger_fatal("__mysql_dll not null"); + logger_fatal("mysql(%s) to be loaded again!", + __mysql_path.c_str()); const char* path; const char* ptr = acl::db_handle::get_loadpath(); @@ -100,6 +103,8 @@ static void __mysql_dll_load(void) if (__mysql_dll == NULL) logger_fatal("load %s error: %s", path, acl_last_serror()); + __mysql_path = path; + __mysql_libversion = (mysql_libversion_fn) acl_dlsym(__mysql_dll, "mysql_get_client_version"); if (__mysql_libversion == NULL) @@ -615,6 +620,28 @@ int db_mysql::affect_count() const return (int) __mysql_affected_rows(conn_); } +bool db_mysql::begin_transaction() +{ + const char* sql = "start transaction"; + if (sql_update(sql) == false) + { + logger_error("%s error: %s", sql, get_error()); + return false; + } + return true; +} + +bool db_mysql::commit() +{ + const char* sql = "commit"; + if (sql_update(sql) == false) + { + logger_error("%s error: %s", sql, get_error()); + return false; + } + return true; +} + } // namespace acl #else @@ -668,6 +695,16 @@ bool db_mysql::sql_update(const char*) return false; } +bool db_mysql::begin_transaction() +{ + return false; +} + +bool db_mysql::commit() +{ + return false; +} + int db_mysql::affect_count() const { return 0; diff --git a/lib_acl_cpp/src/db/db_sqlite.cpp b/lib_acl_cpp/src/db/db_sqlite.cpp index 8035a5591..5ed5b5131 100644 --- a/lib_acl_cpp/src/db/db_sqlite.cpp +++ b/lib_acl_cpp/src/db/db_sqlite.cpp @@ -42,6 +42,7 @@ static acl_pthread_once_t __sqlite_once = ACL_PTHREAD_ONCE_INIT; static ACL_DLL_HANDLE __sqlite_dll = NULL; + static acl::string __sqlite_path; // 程序退出释放动态加载的库 static void __sqlite_dll_unload(void) @@ -50,7 +51,7 @@ { acl_dlclose(__sqlite_dll); __sqlite_dll = NULL; - logger("sqlite3.dll unload ok"); + logger("%s unload ok", __sqlite_path.c_str()); } } @@ -58,7 +59,11 @@ static void __sqlite_dll_load(void) { if (__sqlite_dll != NULL) - logger_fatal("__sqlite_dll not null"); + { + logger_warn("sqlite(%s) to be loaded again!", + __sqlite_path.c_str()); + return; + } const char* path; const char* ptr = acl::db_handle::get_loadpath(); @@ -75,6 +80,8 @@ if (__sqlite_dll == NULL) logger_fatal("load %s error: %s", path, acl_last_serror()); + __sqlite_path = path; + __sqlite3_libversion = (sqlite3_libversion_fn) acl_dlsym(__sqlite_dll, "sqlite3_libversion"); if (__sqlite3_libversion == NULL) diff --git a/lib_acl_cpp/src/http/HttpServletResponse.cpp b/lib_acl_cpp/src/http/HttpServletResponse.cpp index ce59ce521..6d812170f 100644 --- a/lib_acl_cpp/src/http/HttpServletResponse.cpp +++ b/lib_acl_cpp/src/http/HttpServletResponse.cpp @@ -157,8 +157,8 @@ bool HttpServletResponse::sendHeader(void) header_->set_content_type(buf); - // 铏界劧鏈嶅姟绔湪鍝嶅簲澶翠腑璁剧疆浜 gzip 鍘嬬缉鏂瑰紡锛屼絾濡傛灉璇锋眰绔笉鎺ユ敹 - // gzip 鍘嬬缉鏁版嵁锛屽垯闇瑕佷粠鍝嶅簲澶翠腑绂佹 + // 虽然服务端在响应头中设置了 gzip 压缩方式,但如果请求端不接收 + // gzip 压缩数据,则需要从响应头中禁止 if (header_->is_transfer_gzip() && request_) { bool accept_gzip = false;