OCCI小程式示例

来源:https://www.cnblogs.com/liujiashuai/archive/2018/02/02/8406128.html
-Advertisement-
Play Games

1. 引言 1.1. 背景 本文延續《OCCI開發環境的安裝和配置》一文,目的主要是搭建一個可用的OCCI開發環境。作為環境的驗證,本文給出一個小例子,獲取Oracle資料庫的系統時間的小程式,在OCCI環境開發和運行。 同時,總結一下在測試過程中遇到的所有問題和使用的知識,不僅限於OCCI,包括I ...


1.  引言

1.1.  背景

本文延續《OCCI開發環境的安裝和配置》一文,目的主要是搭建一個可用的OCCI開發環境。作為環境的驗證,本文給出一個小例子,獲取Oracle資料庫的系統時間的小程式,在OCCI環境開發和運行。

同時,總結一下在測試過程中遇到的所有問題和使用的知識,不僅限於OCCI,包括IDE、編譯器、操作系統(Linux)。

1.2.  系統環境

資料庫:Oracle12c

客戶端:instantclient_12_2

操作系統:Ubuntu16.04.3 Linux kernel 4.4.0-112-generic

IDE:Eclipse Oxygen.2 Release (4.7.2) Build id: 20171218-0600

編譯器:gcc 4.8.5

2.  程式代碼

2.1.  主程式

main.cpp

 1 #include <string>
 2 #include <iostream>
 3 #include "CMyDatabase.h"
 4  
 5 Int main (int argc, char * argv[])
 6 
 7 {
 8 
 9   CMyDatabase stdb;
10   std::string user = "system";
11   std::string passwd = "Oracle123";
12   std::string connStr = "storcdb";
13  
14   stdb.connect (user, passwd, connStr);
15   stdb.getSystemDateFromDatabase ();
16 
17   return 0;
18 
19 }

主程式很簡單。CMyDatabase類是我封裝的Oracle資料庫訪問用的類,在主程式中,我們只要知道我們通過這個類連接資料庫(connect),並且可以獲得資料庫系統的時間(getSystemDateFromDatabase)。

在連接資料庫時,我們需要提供資料庫訪問的用戶名和密碼,以及標識資料庫的網路服務名稱或者說資料庫連接字元串。可以參考《OCCI開發環境的安裝和配置》一文中的4.1節以及4.2節末尾使用sqlplus測試資料庫連接的部分內容。

2.2.  CMyDatabase

CMyDatabase類主要封裝了Oracle的Environment和Connection類,由於這兩個類(OCCI中其他類如Statement類也存在這種情況)的創建都是以指針的方式返回,需要進行銷毀,所以,將這種資源類封裝在管理類裡面,本例為CMyDatabase,由CMyDatabase負責創建和銷毀Environment和Connection類的實例,從而保證資源的正確使用,即創建和銷毀。

CMyDatabase.h

 1 #ifndef CMYDATABASE_H_
 2 #define CMYDATABASE_H_
 3 
 4 #include <string>
 5 #include <iostream>
 6 #include <occi.h>
 7 using namespace oracle::occi;
 8 
 9 class CMyDatabase
10 {
11 public:
12     CMyDatabase() : m_env(NULL), m_conn(NULL)
13     {
14     }
15     virtual ~CMyDatabase();
16     void connect(const std::string & user, const std::string & passwd,
17         const std::string & connStr);
18     void getSystemDateFromDatabase();
19 private:
20     Environment * m_env;
21     Connection * m_conn;
22 };
23 
24 #endif /* CMYDATABASE_H_ */

 

CMyDatabase.cpp

 1 #include "CMyDatabase.h"
 2 
 3 CMyDatabase::~CMyDatabase()
 4 {
 5     if (NULL != m_conn)
 6     {
 7         m_env->terminateConnection(m_conn);
 8         m_conn = NULL;
 9     }
10     if (NULL != m_env)
11         Environment::terminateEnvironment(m_env);
12 }
13 
14 void CMyDatabase::connect(const std::string & user, const std::string & passwd,
15     const std::string & connStr)
16 {
17     try
18     {
19         m_env = Environment::createEnvironment();
20         m_conn = m_env->createConnection(user, passwd, connStr);
21     }
22     catch (SQLException & e)
23     {
24         std::cout << "using " << user << "/" << passwd << "@" << connStr
25                 << " connect to database." << std::endl;
26         std::cout << "*** " << e.getErrorCode() << ": " << e.getMessage()
27                 << std::endl;
28     }
29 }
30 
31 void CMyDatabase::getSystemDateFromDatabase()
32 {
33     Statement * stmt = m_conn->createStatement();
34     ResultSet * rs = stmt->executeQuery(
35             "select to_char(sysdate, 'YYYY-MM-DD HH:MI:SS') from dual");
36     rs->next();
37     std::cout << rs->getString(1) << std::endl;
38     stmt->closeResultSet(rs);
39     m_conn->terminateStatement(stmt);
40 }

在void CMyDatabase::getSystemDateFromDatabase();函數中創建一個Statement對象,並執行一個獲得資料庫系統時間的SQL語句,執行之後會返回一個ResultSet(結果集),訪問結果集之前需要調用next()函數,實際上只有該函數返回Status::DATA_AVAILABLE(如果是流類型的數據,返回值為Statue::    STREAM_DATA_AVAILABLE)的時候才表明有數據,可以獲得結果集中的數據。另外,對於資料庫函數的使用要將其放入異常捕獲代碼塊中,就像CMyDatabase::connect函數中寫的一樣。在這裡我省略的必要的判斷,在正式的代碼中一定不要忘記。

 

3.  問題分析

本文不打算講解使用Eclipse創建這個C++項目的過程,我相信有很多資料會講,而且,我相信你是一個有經驗的人,即使你是初學者,我認為通過摸索你也能夠很快地把項目正確地創建出來。我們都是程式員,有這智商。

因此,這一章主要講解一下這個例子中所遇到的一些問題,希望對大家有所幫助。

3.1.  安裝Ubuntu16.04

如果你需要安裝一個全新的Ubuntu16.04,我建議你直接到官方網站下載的最新版本的Ubuntu16.04.3(ubuntu-16.04.3-desktop-amd64.iso),當然,也可以嘗試更高的版本。在最初的16.04安裝包中存在問題,執行系統更新(sudo apt-get update)的時候會崩潰,需要將libappstream3包清除掉(sudo apt-get purge libappstream3)或者手工下載libappstream包進行安裝,解決該問題。

3.2.  為什麼是g++-4.8

當你安裝了Ubuntu16.04或者更高的版本時,你的系統預設或者執行sudo apt-get install g++之後所安裝的g++版本均在5.0以上。由於OCCI庫是在gcc-4下編譯,在gcc-5(5以上版本未測試)上編譯後,程式執行會崩潰。具體的原因我目前還不清楚,推測與兩個版本的std::string實現有關。

基於以上原因,我們需要為系統安裝g++-4.8:

sudo apt-get install g++-4.8

如果是經過g++ 5.4編譯過的程式,在連接資料庫時會收到ORA-24960異常,並且程式崩潰轉儲。具體異常錯誤如下:

ORA-24960: the attribute  OCI_ATTR_USERNAME is greater than the maximum allowable length of 255 

......

 

 

 

如果使用makefile來編譯,那麼制定編譯和鏈接工具為g++-4.8就可以了;如果使用Eclipse編譯,同樣也需要配置編譯和鏈接工具為g++-4.8。在項目上點擊右鍵選擇Properties --> C/C++ Build --> Settings,修改如下圖標記的位置為g++-4.8。

 

3.3.  'std::string' is ambiguous '

當在系統中安裝了g++-4.8之後,由於有多個gcc版本的存在,Eclipse會找到多個gcc版本的頭文件,所以,Eclipse會對你用到的類型提示ambiguous,例如std::string。

當右鍵點擊查看定義時,會彈出選擇具體頭文件的視窗,如下圖所示。

如同配置g++-4.8一樣,在Eclipse項目上滑鼠右鍵點擊打開Properties --> C/C++ Build --> Settings,按照下圖示例設置“模棱兩可”的頭文件引用。

 

對於這個“include files”設置,除瞭解決頭文件選擇的二義性之外,是否還有其他什麼作用?

4.  知識延伸

4.1.  SONAME

在前一篇文章《OCCI開發環境的安裝和配置》中提到動態庫libclntshcore.so.12.1是否需要建立沒有版本號的符號鏈接這個問題。通過對動態庫的SONAME進行分析可以得到答案。

使用readelf查看動態庫的SONAME,我們會發現libcclntshcore.so.12.1動態庫的SONAME為“libclntshcore.so.12.1”,如下圖:

readelf -d /opt/oracle/instantclient_12_2/libclntshcore.so

Dynamic section at offset 0x3b8f80 contains 30 entries:

  標記        類型                         名稱/0x0000000000000001 (NEEDED)             共用庫:[libdl.so.2]

 0x0000000000000001 (NEEDED)             共用庫:[libm.so.6]

 0x0000000000000001 (NEEDED)             共用庫:[libpthread.so.0]

 0x0000000000000001 (NEEDED)             共用庫:[libnsl.so.1]

 0x0000000000000001 (NEEDED)             共用庫:[librt.so.1]

 0x0000000000000001 (NEEDED)             共用庫:[libaio.so.1]

 0x0000000000000001 (NEEDED)             共用庫:[libresolv.so.2]

 0x0000000000000001 (NEEDED)             共用庫:[libc.so.6]

 0x0000000000000001 (NEEDED)             共用庫:[ld-linux-x86-64.so.2]

 0x0000000000000001 (NEEDED)             共用庫:[libgcc_s.so.1]

 0x000000000000000e (SONAME)             Library soname: [libclntshcore.so.12.1]

……

當我們的代碼不直接引用該動態庫時,我們就不需要定義一個沒有版本號的符號鏈接,因為使用到該動態庫的其他程式會直接使用SONAME定義的名稱找到該動態庫。例如,我們readelf查看一下libclntsh.so.12.1的動態庫引用情況:

readelf -d /opt/oracle/instantclient_12_2/libclntsh.so

Dynamic section at offset 0x3859bc0 contains 35 entries:

  標記        類型                         名稱/0x0000000000000001 (NEEDED)             共用庫:[libmql1.so]

 0x0000000000000001 (NEEDED)             共用庫:[libipc1.so]

 0x0000000000000001 (NEEDED)             共用庫:[libnnz12.so]

 0x0000000000000001 (NEEDED)             共用庫:[libons.so]

 0x0000000000000001 (NEEDED)             共用庫:[libdl.so.2]

 0x0000000000000001 (NEEDED)             共用庫:[libm.so.6]

 0x0000000000000001 (NEEDED)             共用庫:[libpthread.so.0]

 0x0000000000000001 (NEEDED)             共用庫:[libnsl.so.1]

 0x0000000000000001 (NEEDED)             共用庫:[librt.so.1]

 0x0000000000000001 (NEEDED)             共用庫:[libaio.so.1]

 0x0000000000000001 (NEEDED)             共用庫:[libresolv.so.2]

 0x0000000000000001 (NEEDED)             共用庫:[libc.so.6]

 0x0000000000000001 (NEEDED)             共用庫:[ld-linux-x86-64.so.2]

 0x0000000000000001 (NEEDED)             共用庫:[libclntshcore.so.12.1]

 0x0000000000000001 (NEEDED)             共用庫:[libgcc_s.so.1]

 0x000000000000000e (SONAME)             Library soname: [libclntsh.so.12.1]

 

從命令結果來分析,libclntsh.so.12.1使用了libclntshcore.so.12.1,它可以通過SONAME找到這個文件。

對於libclntsh.so.12.1了來說,我們需要在程式中引用該動態庫,所以,需要為該動態庫文件創建符號鏈接,使編譯器可以找到該文件。

這種通過SONAME來確定具體的動態庫的方式,使得在同一個操作系統上存在同一個動態庫的多個版本成為可能,滿足不同應用對不同版本動態庫的依賴要求。

SONAME是在編譯動態庫時指定的,名字與文件名可以不完全一致。

前一篇《OCCI開發環境的安裝和配置》


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • shell 什麼是shell bash shell bash的配置文件 shell 登錄兩種方式 bash中的退出時的任務 ...
  • 編譯安裝 程式包編譯 編譯安裝 ...
  • I2C設備驅動要使用i2c_driver和i2c_client數據結構並填充i2c_driver中的成員函數。i2c_client一般被包含在設備的私有信息結構體xxx_data中,而i2c_driver則適合被定義為全局變數並初始化。下麵提供i2c_driver的初始化模版: 1 Linux I2 ...
  • 1 I2C子系統框架 Linux I2C子系統分成三部分:I2C核心層、I2C匯流排驅動和I2C設備驅動。 (1)I2C核心層 I2C核心提供了I2C匯流排驅動和設備驅動的註冊、註銷方法,I2C通信方法(即algorithm)上層的與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼等。核心層的代 ...
  • 1 I2C匯流排 IIC匯流排由數據線SDA和時鐘線SCL兩條線構成通信線,既可以發送數據,又可以接收數據。 IIC匯流排典型電路:SDA和SCL連接線上有兩個上拉電阻,所有的IIC設備並聯在匯流排上。 2 I2C信號 IIC匯流排通信有著嚴格的時序,如果時序錯誤將會無法通信。IIC匯流排在傳送數據的過程中,有 ...
  • 1 USB請求塊 USB請求塊是USB設備驅動中用來描述與USB設備通信所用的基本載體和核心數據結構。 2 URB處理流程 USB設備中的每個端點都處理一個URB隊列,在隊列被清空之前,一個URB的典型生命周期如下: (1)使用usb_alloc_urb來分配一個URB。 函數原形 struct u ...
  • 一 概述 在資料庫方面,對於非DBA的程式員來說,事務與鎖是一大難點,針對該難點,本篇文章試圖採用圖文的方式來與大家一起探討。 “淺談SQL Server 事務與鎖”這個專題共分兩篇,上篇主講事務及事務一致性問題,並簡略的提及一下鎖的種類和鎖的控制級別。 下篇主講SQL Server中的鎖機制,鎖控 ...
  • Daemon Fault Tolerance Storm有一些不同的守護進程 Nimbus負責調度workers supervisors負責運行和殺死workers log views負責訪問日誌 UI負責顯示集群的狀態 What happens when a worker dies? 當一個wor ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...