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

目录

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_ORA8I

int 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 tracing
   0x2 | // 2nd level of tracing
   0x4 | // 3rd level of tracing
   0x8 | // 4th level of tracing
   0x10; // 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 object
	std::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 ODBC
	m_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 ODBC
		m_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 variable
		db.set_max_long_size(1024*1024); // set maximum long string size for connect object

		otl_stream pRecordSet(1, // buffer size needs to be set to 1
					szSQL.c_str(),
					// SELECT statement
					db // connect object
					); 
					// create select stream
		int 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  0
		if (info.m_nOperType == 3)
			sprintf_s(szSQL, sizeof(szSQL),"delete from tbSyncKoalaPerson where nID=%d OR nOperType=3", info.m_nID);
		else
			sprintf_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;
#else
		otl_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 1
					szSQL,
					// SELECT statement
					db // connect object
					); 
					// create select stream

		if (!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++开发中使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/952874.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

高级生化大纲

一&#xff0c;蛋白质化学&#xff1a; 蛋白质分离是生物化学和分子生物学研究中的一项基本技术&#xff0c;用于根据蛋白质的物理和化学特性将其从混合物中分离出来。 1. 离心分离法 离心分离法利用离心力来分离不同质量或密度的颗粒和分子。 差速离心&#xff1a;通过逐…

linux网络 | http结尾、理解长连接短链接与cookie

前言&#xff1a;本节是http章节的最后一部分&#xff0c;主要解释一些小概念。讲解到了HTTP的方法&#xff0c;表单&#xff0c; 重定向等等。 现在废话不多说&#xff0c; 开始我们的学习吧。 ps&#xff1a;本节内容都是概念&#xff0c; 知道就行&#xff0c; 友友们放心观…

金融项目实战 03|JMeter脚本实现手工接口测试

目录 一、环境说明 1、项目环境搭建 2、Mock说明 二、构造测试数据 1、通过系统页面构造 2、通过接口构造 3、通过数据库构造【推荐】 4、案例&#xff1a;构造借款业务数据 三、JMeter执行接口测试用例 1、获取图片验证码、获取短信验证码 2、注册脚本 3、登录脚本…

点赞系统设计(微服务)

点赞业务是一个常见的社交功能&#xff0c;它允许用户对其他用户的内容&#xff08;如帖子、评论、图片等&#xff09;表示喜欢或支持。在设计点赞业务时&#xff0c;需要考虑以下几个方面&#xff1a; 一、业务需求 点赞业务需要满足以下特性&#xff1a; 通用&#xff1a;…

网络原理一>UDP协议详解

UDP和TCP都是应用层中的重要协议&#xff0c;如果做基础架构开发&#xff0c;会用得多一些。 这一篇我们先简单聊一下的UDP TCP格式呈现&#xff1a; 我们知道UDP是一种无连接&#xff0c;面向数据报&#xff0c;全双工&#xff0c;不可靠传输特性的网络协议。 基本格式如图…

时空笔记:CBEngine(微观交通模拟引擎)

CBEngine 是一个微观交通模拟引擎&#xff0c;可以支持城市规模的道路网络交通模拟。CBEngine 能够快速模拟拥有数千个交叉路口和数十万辆车辆的道路网络交通。 以下内容基本翻译自CBEngine — CBLab 1.0.0 documentation 1 模拟演示 1.0 模拟演示结构 config.cfg 定义了 roa…

金融项目实战 04|JMeter实现自动化脚本接口测试及持续集成

目录 一、⾃动化测试理论 二、自动化脚本 1、添加断言 1️⃣注册、登录 2️⃣认证、充值、开户、投资 2、可重复执行&#xff1a;清除测试数据脚本按指定顺序执行 1️⃣如何可以做到可重复执⾏&#xff1f; 2️⃣清除测试数据&#xff1a;连接数据库setup线程组 ①明确…

20250112面试鸭特训营第20天

更多特训营笔记详见个人主页【面试鸭特训营】专栏 250112 1. TCP 和 UDP 有什么区别&#xff1f; 特性TCPUDP连接方式面向连接&#xff08;需要建立连接&#xff09;无连接&#xff08;无需建立连接&#xff09;可靠性可靠的&#xff0c;提供确认、重传机制不可靠&#xff0c…

导出文件,能够导出但是文件打不开

背景&#xff1a; 在项目开发中&#xff0c;对于列表的查询&#xff0c;而后会有导出功能&#xff0c;这里导出的是一个excell表格。实现了两种&#xff0c;1.导出的文件&#xff0c;命名是前端传输过去的&#xff1b;2.导出的文件&#xff0c;命名是根据后端返回的文件名获取的…

马斯克的Grok-2 Beta APP在苹果应用商店上限了,Grok-2安装尝鲜使用教程

马斯克的Grok-2 Beta APP 已经上线苹果商城了&#xff0c;移动端的Grok挺好用的&#xff01;无需登录即可使用&#xff01; &#xff08;文末有安装教程&#xff09; 实测之后&#xff0c;Grok-2 绘画方面个人感觉比GPT-4的绘画还要强一些。而且速度还挺快&#xff0c;可以多次…

《机器学习》——sklearn库中CountVectorizer方法(词频矩阵)

CountVectorizer方法介绍 CountVectorizer 是 scikit-learn 库中的一个工具&#xff0c;它主要用于将文本数据转换为词频矩阵&#xff0c;而不是传统意义上的词向量转换&#xff0c;但可以作为词向量转换的一种基础形式。用于将文本数据转换为词频矩阵&#xff0c;它是文本特征…

CV 图像处理基础笔记大全(超全版哦~)!!!

一、图像的数字化表示 像素 数字图像由众多像素组成&#xff0c;是图像的基本构成单位。在灰度图像中&#xff0c;一个像素用一个数值表示其亮度&#xff0c;通常 8 位存储&#xff0c;取值范围 0 - 255&#xff0c;0 为纯黑&#xff0c;255 为纯白。例如&#xff0c;一幅简单的…

支持向量回归(SVR:Support Vector Regression)用于A股数据分析、预测

简单说明 支持向量回归是一种用来做预测的数学方法,属于「机器学习」的一种。 它的目标是找到一条「最合适的线」,能够大致描述数据点的趋势,并允许数据点离这条线有一定的误差(不要求所有点都完全落在这条线上)。 可以把它想象成:找到一条「宽带」或「隧道」,大部分…

ollama教程(window系统)

前言 在《本地大模型工具哪家强&#xff1f;对比Ollama、LocalLLM、LM Studio》一文中对比了三个常用的大模型聚合工具优缺点&#xff0c;本文将详细介绍在window操作系统下ollama的安装和使用。要在 Windows 上安装并使用 Ollama&#xff0c;需要依赖 NVIDIA 显卡&#xff0c…

Flink系统知识讲解之:容错与State状态管理

Flink系统知识之&#xff1a;容错与State状态管理 状态在Flink中叫作State&#xff0c;用来保存中间计算结果或者缓存数据。根据是否需要保存中间结果&#xff0c;分为无状态计算和有状态计算。对于流计算而言&#xff0c;事件持续不断地产生&#xff0c;如果每次计算都是相互…

DolphinScheduler自身容错导致的服务器持续崩溃重大问题的排查与解决

01 问题复现 在DolphinScheduler中有如下一个Shell任务&#xff1a; current_timestamp() { date "%Y-%m-%d %H:%M:%S" }TIMESTAMP$(current_timestamp) echo $TIMESTAMP sleep 60 在DolphinScheduler将工作流执行策略设置为并行&#xff1a; 定时周期调度设置…

ASP.NET Core 实现微服务 - Elastic APM

这次要给大家介绍的是Elastic APM &#xff0c;一款应用程序性能监控组件。APM 监控围绕对应用、服务、容器的健康监控&#xff0c;对接口的调用链、性能进行监控。在我们实施微服务后&#xff0c;由于复杂的业务逻辑&#xff0c;服务之间的调用会像蜘蛛网一样复杂。有了调用链…

25/1/12 嵌入式笔记 学习esp32

了解了一下位选线和段选线的知识&#xff1a; 位选线&#xff1a; 作用&#xff1a;用于选择数码管的某一位&#xff0c;例如4位数码管的第1位&#xff0c;第2位&#xff09; 通过控制位选线的电平&#xff08;高低电平&#xff09;&#xff0c;决定当前哪一位数码管处于激活状…

IMX6U Qt 开发环境

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、交叉编译 1. 安装通用 ARM 交叉编译工具链 2. 安装 Poky 交叉编译工具链 二、编译出厂源码 1. U-boot 2. 内核和模块 3. 编译出厂 Qt GUI 综合 Demo 前言…

【Oracle专栏】2个入参,生成唯一码处理

Oracle相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 1.背景 业务需要&#xff1a;2个参数&#xff0c;如 aidbankid &#xff0c;两个值是联合主键&#xff0c;需要生成一个固定唯一码&#xff0c;长度有限制32位&#xff0c;为了…