一个好用的C++数据库操作库:OTL

server/2025/1/13 15:57:28/

目录

1.简介

2.OTL库的核心类

3.OTL使用

4.使用OTL时注意事项

4.1.多线程初始化

4.2.OTL支持连接池

4.3.大字段的读取方式

4.4.指定数据库类型

4.5.异常处理

5.下载地址

6.总结


1.简介

        OTL(Oracle, ODBC and DB2-CLI Template Library)是一个专为C++开发者设计的通用数据库操作模板库,它支持时下流行的大多数数据库,例如:Oracle、Mysql、PostgreSql、Sybase、Sqllite、MS ACCESS、Firebird等。并且它有跨平台的特性,使用起来也非常简单,在Windows、Linux、MacOS上都可以使用。

        OTL是C++写的,based on templates, 只有一个头文件,大小只有800K+。使用方便,性能也很不错。它简化了数据库编程,提供了一种面向对象的方式来处理SQL语句和数据库连接。OTL库的核心是一个单一的头文件(如otlv4.h),只需在代码中包含该文件,就能利用其提供的功能。此外,OTL库还提供了丰富的功能,包括对多种数据库的支持、流的概念、对大型对象的处理以及国际化支持等。

        它的特点有:

        1)跨平台性:OTL库是纯C++编写的,因此可以在多种操作系统上运行,如Windows、Linux、Unix和MacOSX等。

        2)高效性:OTL库通过模板和底层数据库API的封装,提供了近乎直接调用数据库API的性能。

        3)用性:OTL库提供了简洁的接口和面向对象的设计,使得数据库操作变得更加直观和高效。

        4)丰富的功能:OTL库支持多种数据库类型、流的概念、对大型对象的处理、国际化支持等高级特性。

2.OTL库的核心类

1.otl_connect类:用于建立和管理数据库连接。

2.otl_stream类:是OTL库的核心流对象,用于执行SQL命令和绑定变量。它支持读写操作,可以方便地与STL容器配合使用。

3.otl_exception类:用于处理OTL库中的异常。当OTL流操作可能抛掷异常时,必须使用try/catch块来包裹OTL流的使用代码。

4.otl_long_stringotl_long_unicode_string类:用于存储和操作大型对象(LOBs),如BLOBs和CLOBs。

3.OTL使用

        otl使用简单,只有一个头文件otlv4.h,通常我们在使用的时候只需要在项目头文件中包含:#include <otlv4.h>。

        otl是根据宏定义来判断使用的是什么数据库驱动处理数据,例如我们需要连接的是postgepSql时,需要使用宏定义:
OTL_ODBC_POSTGRESQL或是OTL_ODBC(ODBC 为异构数据库访问提供统一接口)。

        以下是一个简单的使用OTL库进行数据库操作的示例:

#include "otlv4.h"// 指定数据库类型,例如连接Oracle数据库
#define OTL_ORA8Iint main() {try {// 创建数据库连接otl_connect db;db.rlogon("user", "password", "database");// 插入数据otl_stream o(1, "insert into test_tab values(:f1<int>, :f2<char[31]>)", db);int i = 1;char str[32] = "Test";o << i << str;// 执行查询otl_stream si(50, "select * from test_tab", db);while (!si.eof()) {int id;char name[32];si >> id >> name;std::cout << "ID: " << id << ", Name: " << name << std::endl;}// 断开数据库连接db.logoff();} catch (otl_exception& e) {std::cerr << "OTL exception: " << e.msg << std::endl;std::cerr << "SQL that caused the error: " << e.stm_text << std::endl;}return 0;
}

        从上面示例可以看到,使用OTL跟其它库操作数据库的步骤是差不多的,先连接数据库,然后对数据库进行操作,最后关闭数据库连接;但是在实际的项目中,用到数据库的地方比较多,还需要对数据库的操作进行封装。

        以下是实际开发项目上的封装的类,拿过去就可以用。

 连接类:COTLWrapper

OTLConnect.h

#ifndef _OTL_CONNECT_H_
#define _OTL_CONNECT_H_
#define OTL_ODBC // Compile OTL 4/ODBC, MS SQL 2008
#define OTL_STL // Turn on STL features
#include "DBCommon.h"
#include <string>
#include <iostream>
#include <stdio.h>
//#include "Log.h"
using namespace std;
/*
unsigned int my_trace_level=0x1 | // 1st level of tracing0x2 | // 2nd level of tracing0x4 | // 3rd level of tracing0x8 | // 4th level of tracing0x10; // 5th level of tracing
// each level of tracing is represented by its own bit, 
// so levels of tracing can be combined in an arbitrary order.#define OTL_TRACE_LEVEL  0x1F // enables OTL tracing, and uses my_trace_level as a trace control variable.#define OTL_TRACE_STREAM theLog // directs all OTL tracing to cerr#define OTL_TRACE_LINE_PREFIX "MY OTL TRACE ==> " // redefines the default OTL trace line prefix. This #define is optional*/#define OTL_ODBC_MSSQL_2008 // Compile OTL 4/ODBC, MS SQL 2008
//#define OTL_ODBC // Compile OTL 4/ODBC. Uncomment this when used with MS SQL 7.0/ 2000
#include "otlv4.h"class COTLWrapper
{friend class IOTLTableBase;public:COTLWrapper();COTLWrapper(const stDatabaseConfig& config);virtual ~COTLWrapper();public:static void InitOTL();public:BOOL  Connect(const stDatabaseConfig& stSrvConfig);BOOL  IsConnected() const {return m_bConnected;}std::string  GetErrorMsg() const {return m_szError;}void  Close();protected:BOOL  Connect();private:BOOL m_bConnected;stDatabaseConfig   m_stDBConfig;otl_connect m_db; // connect objectstd::string m_szError;
};#endif

OTLConnect.cpp

#include "stdafx.h"
#include "OTLConnect.h"
//#include <assert.h>
//#include "Log.h"COTLWrapper::COTLWrapper(const stDatabaseConfig& config)
: m_bConnected(false),m_stDBConfig(config)
{
}COTLWrapper::COTLWrapper()
: m_bConnected(false)
{
}COTLWrapper::~COTLWrapper()
{Close();
}void COTLWrapper::InitOTL()
{otl_connect::otl_initialize(); // initialize ODBC environment
}BOOL  COTLWrapper::Connect(const stDatabaseConfig& stSrvConfig)
{m_stDBConfig = stSrvConfig;return Connect();
}void  COTLWrapper::Close()
{m_db.logoff(); // disconnect from ODBCm_bConnected = FALSE;
}BOOL  COTLWrapper::Connect()
{try{char szTemp[255] = { 0 };sprintf_s(szTemp, sizeof(szTemp), "driver=sql server;server=%s;UID=%s;PWD=%s;database=%s", m_stDBConfig.m_szSrvName, m_stDBConfig.m_szUserName, m_stDBConfig.m_szPassword,m_stDBConfig.m_szDBName);m_db.rlogon(szTemp); // connect to ODBCm_bConnected = TRUE;//theLog.Logf("系统初始化1,数据库连接成功");return TRUE;}catch(otl_exception& p){m_szError = (char*)p.msg;//cerr<<p.stm_text<<endl; // print out SQL that caused the error//cerr<<p.var_info<<endl; // print out the variable that caused the error//theLog.Logf("系统初始化1,连接数据库失败,%s", p.msg);return FALSE;}
}

数据访问基类:IOTLTableBase

class  COTLWrapper;
class  IOTLTableBase
{
public:IOTLTableBase(COTLWrapper* pConnection) : m_pConnection(pConnection) {}virtual ~IOTLTableBase() {}protected:BOOL  ConnectDatabase() const;otl_connect&  GetConnection() const;private:COTLWrapper*  m_pConnection;
};
BOOL  IOTLTableBase::ConnectDatabase() const
{if (m_pConnection->IsConnected())return TRUE;return m_pConnection->Connect();
}otl_connect&  IOTLTableBase::GetConnection() const
{return m_pConnection->m_db;
}

数据访问类:COTLPersonOper

class COTLPersonOper : public IOTLTableBase
{
public:COTLPersonOper(COTLWrapper* pConnection) : IOTLTableBase(pConnection) {}virtual ~COTLPersonOper() {}public:BOOL LoadOneNoAsyncPersonInfo(stKoalaPersonItem& info);BOOL WriteUploadRemark(const stKoalaPersonItem& info);BOOL LoadOnePersonInfoFromRecord(stDoorRecordItem& info);private:BOOL SaveDataToLocalFile(std::string& szPath, const unsigned char* pData, int nLen) const;
};
///
/**********************************************************************************************tbSyncKoalaPerson.nKKPersonID  一定要用同步表的人员ID,防止在Persons删除人员的时候,Persons表的
人员ID为, 然后再相机里面就删不掉这个人***********************************************************************************************/
BOOL COTLPersonOper::LoadOneNoAsyncPersonInfo(stKoalaPersonItem& info)
{if (!ConnectDatabase())return FALSE;std::string szSQL,szTemp;BOOL bResult = FALSE;try{memset(info.m_szKKIdentifyImage, 0, sizeof(info.m_szKKIdentifyImage));//szSQL = "SELECT top 1 nID,PersonID,nOperType,Name,Date2,CardID,Photo from tbSyncKoalaPerson  \//     LEFT JOIN Persons ON tbSyncKoalaPerson.nKKPersonID = Persons.PersonID  where tbSyncKoalaPerson.bIsKoalaUpdate=0";szSQL = "SELECT top 1 nID, \ISNULL(tbSyncKoalaPerson.nKKPersonID,0) AS RealPersonID,\nOperType AS PersonType, \ISNULL(Name,'') AS PersonName, \ISNULL(Date1,'') AS PersonStartDate,  \ISNULL(Date2,'') AS PersonEndDate, \ISNULL(CardID,0) AS PersonCardID, \Photo AS PersonPhoto   \from tbSyncKoalaPerson  \LEFT JOIN Persons   \ON tbSyncKoalaPerson.nKKPersonID = Persons.PersonID   \where tbSyncKoalaPerson.bIsKoalaUpdate=0";otl_connect& db = GetConnection();otl_long_string byPhoto(1024*1024); // define long string variabledb.set_max_long_size(1024*1024); // set maximum long string size for connect objectotl_stream pRecordSet(1, // buffer size needs to be set to 1szSQL.c_str(),// SELECT statementdb // connect object); // create select streamint nCardID  = 0;int nPersonID = 0;if (!pRecordSet.eof()) // while not end-of-data{pRecordSet>>info.m_nID>>nPersonID>>info.m_nOperType>>info.m_szPersonName>>info.m_szEntryDate>>info.m_szEndDate>>nCardID>>byPhoto;info.m_dwKKPersonID = (DWORD)nPersonID;info.m_dwCardID = (DWORD)nCardID;if (byPhoto.len() > 0){szTemp.empty();if (SaveDataToLocalFile(szTemp, &byPhoto[0], byPhoto.len()))_tcscpy_s(info.m_szKKIdentifyImage, sizeof(info.m_szKKIdentifyImage), szTemp.c_str());}bResult = TRUE;}return bResult;}catch (otl_exception& p){//m_szError = (char*)p.msg;theLog.Logf("获取最新同步记录失败,%s", p.msg);return FALSE;}
}BOOL COTLPersonOper::SaveDataToLocalFile(std::string& szPath, const unsigned char* pData, int nLen) const
{std::string szTemp;if (pData && nLen){szTemp = GetJpgFileName();if (SaveJpgFile(szTemp, (char*)pData, nLen)){szPath = szTemp;return TRUE;}}	return FALSE;
}BOOL COTLPersonOper::WriteUploadRemark(const stKoalaPersonItem& info)
{if (!ConnectDatabase())return FALSE;char szTime[20] = { 0 };char szSQL[255] = { 0 };memset(szTime, 0, sizeof(szTime));GetSysTime(0, szTime, sizeof(szTime));try{
#if  0if (info.m_nOperType == 3)sprintf_s(szSQL, sizeof(szSQL),"delete from tbSyncKoalaPerson where nID=%d OR nOperType=3", info.m_nID);elsesprintf_s(szSQL, sizeof(szSQL),"update tbSyncKoalaPerson set bIsKoalaUpdate=1,nKoalaPersonID=%d,szLastOperTime='%s' where nID=%d AND (nOperType=1 OR nOperType=2)", info.m_dwKoalaPersonID, szTime,info.m_nID);long lResult = otl_cursor::direct_exec( GetConnection(), szSQL);//return (1 == lResult);return TRUE;
#elseotl_connect& db = GetConnection();if (info.m_nOperType == 3){otl_stream o(1,	"delete from tbSyncKoalaPerson where nID=:1<int>  OR nOperType=3", db);o<<info.m_nID;}else{otl_stream o(1, "update tbSyncKoalaPerson set bIsKoalaUpdate=1,szLastOperTime=:1<char[20]> where nID=:2<int> AND (nOperType=1 OR nOperType=2)", db);o<<szTime<<info.m_nID;}return TRUE;
#endif}catch (otl_exception& p){theLog.Logf("更新人员[%d]上传标记失败1,%s", info.m_dwKKPersonID, p.msg);return FALSE;}
}///
BOOL COTLPersonOper::LoadOnePersonInfoFromRecord(stDoorRecordItem& info)
{if (!ConnectDatabase())return FALSE;int nTemp = 0;char szSQL[512] = {0};char szSex[10] = {0};BOOL bResult = FALSE;try{sprintf_s(szSQL, sizeof(szSQL), "SELECT CardID,CardNum,Sex,DeptID,Dept2Index,ISNULL(WorkNum,'') AS PersonWorkNum,ISNULL(Date1,'') AS PersonEntryDate,ISNULL(duty,'') AS PersonDuty,ISNULL(Phone,'') AS PersonPhoto from Persons where PersonID=%d",atol(info.m_szPersonID));otl_connect& db = GetConnection();otl_stream pRecordSet(1, // buffer size needs to be set to 1szSQL,// SELECT statementdb // connect object); // create select streamif (!pRecordSet.eof()) // while not end-of-data{pRecordSet>>nTemp>>info.m_szCardNum>>szSex>>info.m_nDeptID>>info.m_nSubDeptID>>info.m_szWorkNO>>info.m_szEntryTime>>info.m_szDuty>>info.m_szTel;info.m_dwCardID = (DWORD)nTemp;if (_tcslen(info.m_szCardNum) <= 0)_tcscpy_s(info.m_szCardNum, sizeof(info.m_szCardNum), "00000000");info.m_ucGender = (0 == strcmp(szSex, "男"))?0:1;bResult = TRUE;}return bResult;}catch (otl_exception& p){theLog.Logf("获取系统人员信息失败1,%s", p.msg);return FALSE;}
}

4.使用OTL时注意事项

4.1.多线程初始化

static int otl_connect::otl_initialize(const int threaded_mode=0);

如果在多线程环境下使用,threaded_mode设置为1

注意:即使设置为1并不代表就是线程安全(thread-safe)

实际上OTL并不是线程安全的,一个otl_connect只能同时被一个线程使用,如果在多线程环境下使用OTL,需要自己保证otl_connect对象的线程安全

4.2.OTL支持连接池

一般封装OTL连接池的思路:
1) 建立多条与数据库的连接otl_connect ,并根据需要,将连接信息和连接的句柄封装到数据结构中(存放单条连接);
2) 使用连接管理器管理封装好的连接;
3) Sql操作通过流的方式来使用连接
4) 每次执行sql语句时,首先是从现有的连接中找,有,从现有连接中拿一条,替换流;没有即添加,如果添加连接数量超过最大值,等待已有连接运行结束。

        OTL支持stream pool,就是一个池,在一个otl_stream close时,把它放到池中,下次访问时可以从池中获取,实现fast reopen,提高程序性能

        注意:otl_stream_pool是otl_connect的一个成员,所以要在otl_connect锁unlock之前执行otl_stream.close,否则会出现死锁),即使不使用otl_stream_pool,也要在otl_connect锁unlock之前执行otl_stream.close。

        经验证,OTL在多线程环境可以稳定运行

        使用otl_stream_pool可以获得一定的性能提升

4.3.大字段的读取方式

连接的流中需要开启lob_stream模式:set_lob_stream_mode(true)。

otl_lob_stream lob;
otl_long_string long_str(40960);otl_stream >> lob;
while (!lob.eof())
{lob>> long_str;//todo...
}
lob.close();

4.4.指定数据库类型

         在使用OTL库时,需要通过预处理器宏定义来确定连接的目标数据库。例如,#define OTL_ORA8I用于连接Oracle 8i数据库

4.5.异常处理

        OTL流操作可能抛掷异常,因此必须使用try/catch块来包裹OTL流的使用代码,以拦截异常并阻止程序异常终止。

5.下载地址

地址:Oracle, Odbc and DB2-CLI Template Library Programmer's Guide

6.总结

        OTL库是一个功能强大、高效且易用的C++数据库访问库。它提供了丰富的功能和简洁的接口,使得C++开发者能够方便地进行数据库操作。可以使用OTL访问基本上所有的数据库,在你更换数据库时不用修改任何业务代码。

        强烈推荐在C++开发中使用。


http://www.ppmy.cn/server/158028.html

相关文章

AI在零售行业中的应用:提升顾客体验与运营效率

你知道吗&#xff1f;零售行业正悄悄发生着一场革命&#xff01;AI正在改变我们的购物方式&#xff0c;提升体验的同时&#xff0c;还让商家们的运营更高效&#xff01; 1、个性化推荐 AI通过分析你的购物历史和兴趣&#xff0c;精准推荐你喜欢的商品&#xff0c;再也不怕刷到…

开发指南090-使用python做微服务

平台的后台涉及到业务部分的使用java开发&#xff0c;基于springcloud。 涉及到大数据、AI、网页爬虫等领域的使用python开发。 使用python开发微服务过程如下: 1、读取bootstrap.yaml配置 def read_yaml(file): 读取配置文件 with open(file, r, encodingutf-8) as…

RAID储存技术

RAID独立磁盘冗余技术是一种把2个或者多个HDD或SSD合并为一个协调的存储单元或列阵&#xff0c;从而预防数据丢失的技术&#xff0c;其最早由加州大学伯克利分校的计算机科学家David Patterson、Garth Gibson和Randy Katz在1987年提出。他们的研究论文“关于RAID的论证”提出了…

使用Logstash 将Mysql的数据导入ElasticSearch中

目录 1 .Logstash官网 2. 准备工作 3.创建配置文件 4. 启动 5. 版本不一致导致的无法启动 6.目录写错说明 1 .Logstash官网 Logstash&#xff1a;收集、解析和转换日志 | Elastichttps://www.elastic.co/cn/logstash 首先在官网上选择下载 2. 准备工作 Mysql的jar包下…

一个基于Spring Boot的智慧养老平台

以下是一个基于Spring Boot的智慧养老平台的案例代码。这个平台包括老人信息管理、健康监测、紧急呼叫、服务预约等功能。代码结构清晰&#xff0c;适合初学者学习和参考。 1. 项目结构 src/main/java/com/example/smartelderlycare├── controller│ ├── ElderlyCon…

uniapp中rpx和upx的区别

在 UniApp 中&#xff0c;rpx 和 upx 是两种不同的单位&#xff0c;它们的主要区别在于适用的场景和计算方式。 ### rpx&#xff08;Responsive Pixel&#xff09; - **适用场景**&#xff1a;rpx 是一种响应式单位&#xff0c;主要用于小程序和移动端的布局。 - **计算方式**…

HarMonyOS 鸿蒙系统使用 Grid构建网格

网格布局是由“行”和“列”分割的单元格所组成&#xff0c;通过指定“项目”所在的单元格做出各种各样的布局。网格布局具有较强的页面均分能力&#xff0c;子组件占比控制能力&#xff0c;是一种重要自适应布局&#xff0c;其使用场景有九宫格图片展示、日历、计算器等。 Ar…

论文阅读:Searching for Fast Demosaicking Algorithms

今天介绍一篇有关去马赛克的工作&#xff0c;去马赛克是 ISP 流程里面非常重要的一个模块&#xff0c;可以说是将多姿多彩的大千世界进行色彩还原的重要一步。这篇工作探索的是如何从各种各样的去马赛克算法中&#xff0c;选择最佳的一种。 Abstract 本文提出了一种方法&…