Oracle原生开发API(下)

4.1 其他用户操作oracle数据库需要做的事

        项目不会在oracle用户下开发,因为oracle用户可以进入dba,权限太大不安全。那么如果在xianwu用户下执行sqlplus命令会未找到命令,无法操作oracle。

解决:

1)需要设置xianwu环境变量.bash_profile。将oracle用户的以下环境变量复制到xianwu用户:

export ORACLE_BASE=/oracle/base
export ORACLE_HOME=/oracle/home
# 以下的ORACLE_SID,snoracl是字母,11是数字,g是字母。
export ORACLE_SID=snorcl11g
export NLS_LANG='Simplified Chinese_China.AL32UTF8'
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib:/usr/lib:.
export PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin:.

之后可以通过命令:

sqlplus scott/tiger@snorcl11g_136

登录oracle用户(需要在后面加上数据库服务名@snorcl11g_136)。

如果想要不加数据库服务名登录,需要修改一个oracle用户文件的权限。

2)修改/oracle/home/bin/oracle权限

chmod +s oracle

那么xianwu用户可以直接执行sqlplus scott/tiger登录本机oracle数据库。

4.2 C++封装的ooci类

        Oracle提供了C接口(oci库),但是源码使用对于初学者及其不友好,因此从网上找到一大佬的库,将oci封装成两个类,使用方便(如需要封装的cpp库,请私信)。

一、基本操作crud

1.createtable.cpp

/*
 *  程序名:createtable.cpp,此程序演示开发框架操作Oracle数据库(创建表)。
 *  作者:张咸武。
*/
#include "_ooci.h"   // 开发框架操作Oracle的头文件。
using namespace idc;
​
int main(int argc,char *argv[])
{
    connection conn; // 创建数据库连接类的对象。
​
    // 登录数据库,返回值:0-成功,其它-失败。
    // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
    if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
    {
        printf("connect database failed.\n%s\n",conn.message()); return -1;
    }
​
    printf("connect database ok.\n");
​
    sqlstatement stmt;       // 操作SQL语句的对象。
    stmt.connect(&conn);  // 指定stmt对象使用的数据库连接。
    // 准备创建表的SQL语句。
    // 如果SQL语句有错误,prepare()不会返回失败,所以,prepare()不需要判断返回值。
    // 超女表girls,超女编号id,超女姓名name,体重weight,报名时间btime,超女说明memo,超女图片pic。
    stmt.prepare("\
        create table girls(id    number(10),\
                                    name  varchar2(30),\
                                    weight   number(8,2),\
                                    btime date,\
                                    memo  varchar2(300),\
                                    pic   blob,\
                                    primary key (id))");
​
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
    // 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
    if (stmt.execute() != 0)
    {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    printf("create table girls ok.\n");
​
    conn.disconnect();              // 在connection类的析构函数中会自动调用disconnect()方法。
​
    return 0;
}

2.inserttable.cpp

/*
 *  程序名:inserttable.cpp,此程序演示开发框架操作Oracle数据库(向表中插入数据)。
 *  作者:张咸武。
*/
#include "_ooci.h"   // 开发框架操作Oracle的头文件。
using namespace idc;
​
int main(int argc,char *argv[])
{
    connection conn; // 创建数据库连接类的对象。
​
    // 登录数据库,返回值:0-成功,其它-失败。
    // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
    if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
    {
        printf("connect database failed.\n%s\n",conn.message()); return -1;
    }
​
    printf("connect database ok.\n");
​
    // sqlstatement stmt;       // 操作SQL语句的对象。
    // stmt.connect(&conn);  // 指定stmt对象使用的数据库连接。
    sqlstatement stmt(&conn); 
​
    // stmt.prepare("\
    //             insert into girls(id,name,weight,btime,memo) \
    //                 values(1,'西施',48.5,to_date('2000-01-01 12:30:35','yyyy-mm-dd hh24:mi:ss'),\
    //                            '中国排名第一的美女')");
    //     if (stmt.execute() != 0)
    //     {
    //         printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    //     }
​
    //     printf("成功插入了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL影响的记录数。
    
​
    // 准备插入表的SQL语句。
    /*
    // 静态SQL语句,适用于一次性执行的SQL。1)效率不如动态SQL语句高;2)特殊字符不方便处理;3)安全性(SQL注入)。
    for (int ii=10;ii<15;ii++)
    {
        stmt.prepare("\
                insert into girls(id,name,weight,btime,memo) \
                    values(%d,'西施%05dgirl',%.1f,to_date('2000-01-01 12:30:%02d','yyyy-mm-dd hh24:mi:ss'),\
                               '这是''第%05d个超级女生的备注。')",ii,ii,45.35+ii,ii,ii);
​
        // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
        // 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
        if (stmt.execute() != 0)
        {
            printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
        }
​
        printf("成功插入了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL影响的记录数。
    }
    */
​
    // 1)如果字段是字符串型,绑定的变量可以用char[],也可以用string,推荐用char[]。
    // 2)如果字段是字符串型,bindin()的第三个参数填字段的长度,太小可能会有问题,不推荐缺省值2000。
    // 3)动态SQL语句的字段也可以填静态的值。
    // 4)绑定的变量,一般用结构体。
    struct st_girl
    {
        long id;                 // 超女编号,用long数据类型对应Oracle无小数的number(10)。
        char name[31];     // 超女姓名,用char[31]对应Oracle的varchar2(30)。
        double weight;     // 超女体重,用double数据类型对应Oracle有小数的number(8,2)。
        char btime[20];     // 报名时间,用char对应Oracle的date,格式:'yyyy-mm-dd hh24:mi:ssi'。
        char memo[301];  // 备注,用char[301]对应Oracle的varchar2(300)。
    } stgirl;
​
    // 动态SQL语句,适用于多次执行的SQL。
    stmt.prepare("insert into girls(id,name,weight,btime,memo) \
                                         values(:1,:2,:3,to_date(:4,'yyyy-mm-dd hh24:mi:ss'),:5)");   // :1,:2,...,:n可以理解为输入参数。
    stmt.bindin(1,stgirl.id);
    stmt.bindin(2,stgirl.name,30);
    stmt.bindin(3,stgirl.weight);
    stmt.bindin(4,stgirl.btime,19);
    stmt.bindin(5,stgirl.memo,300);          // 字符串的长度可以不指定,缺省是2000,这种用法不严瑾,不建议。
​
    // 对变量赋值,执行SQL语句。
    for (int ii=15;ii<20;ii++)
    {
        // 初始化变量。
        memset(&stgirl,0,sizeof(struct st_girl));
​
        // 为变量赋值。
        stgirl.id=ii;                                                                                 // 超女编号。
        sprintf(stgirl.name,"西施%05dgirl",ii);                                       // 超女姓名。
        stgirl.weight=45.35+ii;                                                              // 超女体重。
        sprintf(stgirl.btime,"2021-08-25 10:33:%02d",ii);                      // 报名时间。
        sprintf(stgirl.memo,"这是'第%05d个超级女生的备注。",ii);         // 备注。
​
        // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
        // 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
        if (stmt.execute()!=0)
        {
            printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
        }
​
        printf("成功插入了%ld条记录。\n",stmt.rpc());    // stmt.m_cda.rpc是本次执行SQL语句影响的记录数。
    }
​
    conn.commit();                // 提交事务。
​
    return 0;
}

3.updatetable.cpp

/*
 *  程序名:updatetable.cpp,此程序演示开发框架操作Oracle数据库(修改表中的数据)。
 *  作者:张咸武。
*/
#include "_ooci.h"   // 开发框架操作Oracle的头文件。
using namespace idc;
​
int main(int argc,char *argv[])
{
    connection conn; // 创建数据库连接类的对象。
​
    // 登录数据库,返回值:0-成功,其它-失败。
    // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
    if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
    {
        printf("connect database failed.\n%s\n",conn.message()); return -1;
    }
​
    printf("connect database ok.\n");
​
    sqlstatement stmt(&conn); 
​
    // 静态SQL语句。
    /*
    stmt.prepare("\
          update girls set name='冰冰',weight=45.2,btime=to_date('2008-01-02 12:30:22','yyyy-mm-dd hh24:mi:ss') where id=10");
    */
​
    struct st_girl
    {
        long id;                 // 超女编号,用long数据类型对应Oracle无小数的number(10)。
        char name[31];     // 超女姓名,用char[31]对应Oracle的varchar2(30)。
        double weight;     // 超女体重,用double数据类型对应Oracle有小数的number(8,2)。
        char btime[20];     // 报名时间,用char对应Oracle的date,格式:'yyyy-mm-dd hh24:mi:ssi'。
        char memo[301];  // 备注,用char[301]对应Oracle的varchar2(300)。
    } stgirl;
    
    // 动态SQL语句。
    stmt.prepare("\
            update girls set name=:1,weight=:2,btime=to_date(:3,'yyyy-mm-dd hh24:mi:ss') where id=:4");  // :1,:2,...,:n可以理解为输入参数。
    stmt.bindin(1,stgirl.name,30);
    stmt.bindin(2,stgirl.weight);
    stmt.bindin(3,stgirl.btime,19);
    stmt.bindin(4,stgirl.id);
​
    // 初始化结构体,为变量赋值。
    memset(&stgirl,0,sizeof(struct st_girl));
    stgirl.id=11;                                                              // 超女编号。
    sprintf(stgirl.name,"幂幂");                                       // 超女姓名。
    stgirl.weight=43.85;                                                 // 超女体重。
    strcpy(stgirl.btime,"2021-08-25 10:33:35");    
​
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
    // 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
    if (stmt.execute() != 0)
    {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    printf("成功修改了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL影响的记录数。
​
    conn.commit();       // 提交事务。
​
    return 0;
}

4.deletetable.cpp

/*
 *  程序名:deletetable.cpp,此程序演示开发框架操作Oracle数据库(删除表中的数据)。
 *  作者:张咸武。
*/
#include "_ooci.h"   // 开发框架操作Oracle的头文件。
using namespace idc;
​
int main(int argc,char *argv[])
{
    connection conn; // 创建数据库连接类的对象。
​
    // 登录数据库,返回值:0-成功,其它-失败。
    // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
    if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
    {
        printf("connect database failed.\n%s\n",conn.message()); return -1;
    }
​
    printf("connect database ok.\n");
​
    sqlstatement stmt(&conn); 
​
    // 静态SQL语句。
    stmt.prepare("delete from girls where id=10");
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
    // 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
    if (stmt.execute() != 0)
    {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    printf("成功删除了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL影响的记录数。
​
    int minid=11,maxid=13;
    
    // 动态SQL语句。
    stmt.prepare("delete from girls where id>=:1 and id<=:2");  // :1,:2,...,:n可以理解为输入参数。
    stmt.bindin(1,minid);
    stmt.bindin(2,maxid);
​
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
    // 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
    if (stmt.execute() != 0)
    {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    printf("成功删除了%ld条记录。\n",stmt.rpc()); // stmt.m_cda.rpc是本次执行SQL影响的记录数。
​
    conn.commit();       // 提交事务。
​
    return 0;
}

5.selecttable.cpp

/*
 *  程序名:selecttable.cpp,此程序演示开发框架操作Oracle数据库(查询表中的数据)。
 *  作者:张咸武。
*/
#include "_ooci.h"   // 开发框架操作Oracle的头文件。
using namespace idc;
​
int main(int argc,char *argv[])
{
    connection conn; // 创建数据库连接类的对象。
​
    // 登录数据库,返回值:0-成功,其它-失败。
    // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
    if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
    {
        printf("connect database failed.\n%s\n",conn.message()); return -1;
    }
​
    printf("connect database ok.\n");
​
    sqlstatement stmt(&conn); 
​
    int minid=14,maxid=16;
    struct st_girl
    {
        long id;                 // 超女编号,用long数据类型对应Oracle无小数的number(10)。
        char name[31];     // 超女姓名,用char[31]对应Oracle的varchar2(30)。
        double weight;     // 超女体重,用double数据类型对应Oracle有小数的number(8,2)。
        char btime[20];     // 报名时间,用char对应Oracle的date,格式:'yyyy-mm-dd hh24:mi:ssi'。
        char memo[301];  // 备注,用char[301]对应Oracle的varchar2(300)。
    } stgirl;
​
    // 准备查询表的SQL语句,prepare()方法不需要判断返回值。
    stmt.prepare("select id,name,weight,to_char(btime,'yyyy-mm-dd hh24:mi:ss'),memo from girls where id>=:1 and id<=:2");
    // 为SQL语句绑定输入变量的地址,bindin()方法不需要判断返回值。
    stmt.bindin(1,minid);
    stmt.bindin(2,maxid);
    // 把查询语句的结果集与变量的地址绑定,bindout()方法不需要判断返回值。
    stmt.bindout(1,stgirl.id);
    stmt.bindout(2,stgirl.name,30);
    stmt.bindout(3,stgirl.weight);
    stmt.bindout(4,stgirl.btime,19);
    stmt.bindout(5,stgirl.memo,300);
​
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
    // 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
    if (stmt.execute() != 0)
    {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    // 本程序执行的是查询语句,执行stmt.execute()后,将会在数据库的缓冲区中产生一个结果集。
    while (true)
    {
        memset(&stgirl,0,sizeof(stgirl));    // 先把结构体变量初始化。
​
        // 从结果集中获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
        // 在实际开发中,除了0和1403,其它的情况极少出现。
        if (stmt.next() !=0) break;
​
        // 把获取到的记录的值打印出来。
        printf("id=%ld,name=%s,weight=%.02f,btime=%s,memo=%s\n",stgirl.id,stgirl.name,stgirl.weight,stgirl.btime,stgirl.memo);
    }
​
    // 请注意,stmt.m_cda.rpc变量非常重要,它保存了SQL被执行后影响的记录数。
    printf("本次查询了girls表%ld条记录。\n",stmt.rpc());
​
    return 0;
}

6.通用步骤

connection conn;            // 创建数据库连接类的对象。
conn.connecttodb();         // 登录数据库,返回值:0-成功,其它-失败。
sqlstatement stmt(&conn);
stmt.prepare();             // 准备SQL语句
stmt.execute() ;            // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。

二、存取大型对象

LOB类型(Large object)

  • CLOB 字符型大型对象(CharacterLarge Object),专用于存储文本文件(受字符集影响、可检索)。varchar2

  • BLOB 二进制大型对象(BinaryLarge Object),可存储任意格式的文件(文本、图、音视频)((不受字符集影响、不可检索)。

  • 如果把大量的文件存放数据库中,浪费存储空间,降低磁盘I/0的效率。数据库采用高性能存储,非普通磁盘。高价聘请美女干苦力。

关于BLOB、CLOB字段的操作稍微麻烦点,一般不用。

等到工作中需要用到的时候再回来参考代码即可。

1.filetoclob.cpp

/*
 *  程序名:filetoclob.cpp,此程序演示开发框架操作Oracle数据库(把文本文件存入数据库表的CLOB字段中)。
 *  作者:张咸武。
*/
#include "_ooci.h"   // 开发框架操作Oracle的头文件。
using namespace idc;
​
int main(int argc,char *argv[])
{
    connection conn; // 创建数据库连接类的对象。
​
    // 登录数据库,返回值:0-成功,其它-失败。
    // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
    if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
    {
        printf("connect database failed.\n%s\n",conn.message()); return -1;
    }
​
    printf("connect database ok.\n");
​
    // 修改girls表结构,增加memo1字段,用于测试。 alter table girls add memo1 clob;
    sqlstatement stmt(&conn); 
    stmt.prepare("insert into girls(id,name,memo1) values(1,'冰冰',empty_clob())");  // 注意:不可用null代替empty_clob()。
    if (stmt.execute()!=0)
    {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    // 使用游标从girls表中提取记录的memo1字段
    stmt.prepare("select memo1 from girls where id=1 for update");
    stmt.bindclob();
​
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
    if (stmt.execute() != 0)
    {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    // 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
    if (stmt.next() != 0) return 0;
​
    // 把磁盘文件memo_in.txt的内容写入CLOB字段,一定要判断返回值,0-成功,其它-失败。
    if (stmt.filetolob("/project/public/db/oracle/memo_in.txt") != 0)
    {
        printf("stmt.filetolob() failed.\n%s\n",stmt.message()); return -1;
    }
​
    printf("文本文件已存入数据库的CLOB字段中。\n");
​
    conn.commit();
​
    return 0;
}

2.clobtofile.cpp

/*
 *  程序名:clobtofile.cpp,此程序演示开发框架操作Oracle数据库(把数据库的CLOB字段提取到文件)。
 *  作者:张咸武。
*/
#include "_ooci.h"   // 开发框架操作Oracle的头文件。
using namespace idc;
​
int main(int argc,char *argv[])
{
    connection conn; // 创建数据库连接类的对象。
​
    // 登录数据库,返回值:0-成功,其它-失败。
    // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
    if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
    {
        printf("connect database failed.\n%s\n",conn.message()); return -1;
    }
​
    printf("connect database ok.\n");
​
    sqlstatement stmt(&conn); 
    stmt.prepare("select memo1 from girls where id=1");
    stmt.bindclob();
​
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
    if (stmt.execute() != 0)
    {
         printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    // 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
    if (stmt.next() != 0) return 0;
​
    // 把CLOB字段中的内容写入磁盘文件,一定要判断返回值,0-成功,其它-失败。
    if (stmt.lobtofile("/project/public/db/oracle/memo_out.txt") != 0)
    {
        printf("stmt.lobtofile() failed.\n%s\n",stmt.message()); return -1;
    }
​
     printf("已把数据库的CLOB字段提取到文件。\n");
​
    return 0;
}

3.filetoblob.cpp

/*
 *  程序名:filetoblob.cpp,此程序演示开发框架操作Oracle数据库(把二进制文件存入数据库的BLOB字段中)。
 *  作者:张咸武。
*/
#include "_ooci.h"   // 开发框架操作Oracle的头文件。
using namespace idc;
​
int main(int argc,char *argv[])
{
    connection conn; // 创建数据库连接类的对象。
​
    // 登录数据库,返回值:0-成功,其它-失败。
    // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
    if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
    {
        printf("connect database failed.\n%s\n",conn.message()); return -1;
    }
​
    printf("connect database ok.\n");
​
    sqlstatement stmt(&conn); 
    stmt.prepare("insert into girls(id,name,pic) values(1,'冰冰',empty_blob())");  // 注意:不可用null代替empty_blob()。
    if (stmt.execute()!=0)
    {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    // 使用游标从girls表中提取记录的pic字段
    stmt.prepare("select pic from girls where id=1 for update");
    stmt.bindblob();
​
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
    if (stmt.execute() != 0)
    {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    // 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
    if (stmt.next() != 0) return 0;
​
    // 把磁盘文件pic_in.jpeg的内容写入BLOB字段,一定要判断返回值,0-成功,其它-失败。
    if (stmt.filetolob("/project/public/db/oracle/pic_in.jpeg") != 0)
    {
        printf("stmt.filetolob() failed.\n%s\n",stmt.message()); return -1;
    }
​
    printf("二进制文件已存入数据库的BLOB字段中。\n");
​
    conn.commit();
​
    return 0;
}

4.blobtofile.cpp

/*
 *  程序名:blobtofile.cpp,此程序演示开发框架操作Oracle数据库(把数据库的BLOB字段提取到文件)。
 *  作者:张咸武。
*/
#include "_ooci.h"   // 开发框架操作Oracle的头文件。
using namespace idc;
​
int main(int argc,char *argv[])
{
    connection conn; // 创建数据库连接类的对象。
​
    // 登录数据库,返回值:0-成功,其它-失败。
    // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
    if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
    {
        printf("connect database failed.\n%s\n",conn.message()); return -1;
    }
​
    printf("connect database ok.\n");
​
    sqlstatement stmt(&conn); 
    stmt.prepare("select pic from girls where id=1");
    stmt.bindblob();
​
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
    if (stmt.execute() != 0)
    {
         printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    // 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
    if (stmt.next() != 0) return 0;
​
    // 把BLOB字段中的内容写入磁盘文件,一定要判断返回值,0-成功,其它-失败。
    if (stmt.lobtofile("/project/public/db/oracle/pic_out.jpeg") != 0)
    {
        printf("stmt.lobtofile() failed.\n%s\n",stmt.message()); return -1;
    }
​
     printf("已把数据库的BLOB字段提取到文件。\n");
​
    return 0;
}

细节

sqlstatement类的isopen()函数:sql语句已准备好返回true!

三、其他的细节

1.静态SQL语句的安全性问题(SQL注入)。

静态执行:select * from operator where 1=1 or ''='';也能查询到,永远成立。

动态sql语句就不会出现这个问题。

2.char和varchar2的问题。

①、固定长度的字符串

固定长度字符串用char表示,当存入数据内容的长度不够时,Oracle将在数据内容后面自动填充空格以达到其固定的长度,例如char(10)总是包含10字节信息。

char字段最多可以存储2000字节的内容。

②、可变长度的字符串

变长度字符串用varchar2表示,与char类型不同,Oracle不会在数据内容后面填充任何内容。

varchar2字段最多可以存储4000字节的内容,从Oracle 12c版本开始,可以存储32767字节的内容。

③、char和varchar2的比较

char(10),如果存入'freecplus',在数据库中将存储'freecplus ',在最后补了一个空格。

varchar2(10),如果存入'freecplus',在数据库中将存储'freecplus',什么也不会补。

在实际开发中,我们并不希望Oracle给字符串后面补空格,那是不是可以弃用char类型呢?不是,我们一般用char类型存放固定大小的数据内容,例如身份证号码,固定是18位的,用char(18)就非常合适,能用varchar2(18)可以存放身份证号码吗?当然可以,但是,char(18)的效率比varchar2(18)的效率要高很多。

总结一下,如果确定、肯定、一定、保证存入字符串的长度是固定不变的,例如性别、身份证号码、手机号码,用char类型,否则用varchar2类型,例如姓名、学历、地址、兴趣爱好等,char虽然死板,但是效率高。但是char有坑~

🔴小小的坑,害了很多人!

create table tt( c1 char(5),c2 varchar2(5)   );
insert into tt values('abs','abc');
select * from tt;

在c1字段中,abc后面又两个空格!而c2字段后面没有空格,也就是:

c1='abc '; c2='abc';

那么执行:

select * from tt where c2='abc';        
select * from tt where c2='abc  ';
select * from tt where c1='abc';        --这个也能查到!!!
select * from tt where c1='abc  ';

1 2 4句都好理解,3也能查询成功!说明char类型比较的时候不比较后面的空格!

而在C++中运行:

a.静态语句与sql直接执行效果一样。

b.动态语句(bindin)执行3语句查询不到。

3.如果处理数字字段的空值。

0不是空,空不是0!

如果想在表中插入空值,那么可以利用oracle的强制类型转换,将数据字段插入空字符串:

insert into tt values('','');   --在c++中操作。

4.PL/SQL的过程语句demo4.cpp

只需在多条sql语句前后加上begin和end。

/*
 *  程序名:demo4.cpp,此程序演示执行PL/SQL过程语句。
 *  作者:张咸武。
*/
#include "_ooci.h"   // 开发框架操作Oracle的头文件。
using namespace idc;
​
int main(int argc,char *argv[])
{
    connection conn; // 创建数据库连接类的对象。
​
    // 登录数据库,返回值:0-成功,其它-失败。
    // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
    if (conn.connecttodb("scott/tiger@snorcl11g_136","Simplified Chinese_China.AL32UTF8") != 0)
    {
        printf("connect database failed.\n%s\n",conn.message()); return -1;
    }
​
    printf("connect database ok.\n");
​
    struct st_girl
    {
        long id;                 // 超女编号,用long数据类型对应Oracle无小数的number(10)。
        char name[31];     // 超女姓名,用char[31]对应Oracle的varchar2(30)。
        double weight;     // 超女体重,用double数据类型对应Oracle有小数的number(8,2)。
        char btime[20];     // 报名时间,用char对应Oracle的date,格式:'yyyy-mm-dd hh24:mi:ssi'。
        char memo[301];  // 备注,用char[301]对应Oracle的varchar2(300)。
    } stgirl;
​
    // 准备PL/SQL语句,如果SQL语句有错误,prepare()不会返回失败,所以,prepare()不需要判断返回值。
    sqlstatement stmt(&conn);       // 操作SQL语句的对象。
    // PL/SQL语句的优点:减少了客户端与数据库的通讯次数,提高了效率。
    stmt.prepare("\
        begin\
            delete from girls where id=:1;\
            insert into girls(id,name) values(:2,:3);\
            update girls set weight=:4 where id=:5;\
        end;");              
    stmt.bindin(1,stgirl.id);
    stmt.bindin(2,stgirl.id);
    stmt.bindin(3,stgirl.name,30);
    stmt.bindin(4,stgirl.weight);
    stmt.bindin(5,stgirl.id);
​
    memset(&stgirl,0,sizeof(struct st_girl));
    stgirl.id=1;
    strcpy(stgirl.name,"冰冰");
    stgirl.weight=49.5;
​
    // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
    // 失败代码在stmt.m_cda.rc中,失败描述在stmt.m_cda.message中。
    if (stmt.execute() != 0)
    {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.sql(),stmt.message()); return -1;
    }
​
    printf("exec pl/sql ok.\n");
​
    printf("影响了%ld条记录。\n",stmt.rpc());    // pl/sql中最后一条SQL语句记录影响记录的行数。
​
    conn.commit();
​
    return 0;
}

4.3 oracle与C++的一些关系(封装的oci为ooci.cpp)

long id;           // 编号,用long数据类型对应Oracle无小数的number(10)。
char name[31];     // 姓名,用char[31]对应Oracle的varchar2(30)。
double weight;     // 体重,用double数据类型对应Oracle有小数的number(8,2)。
char btime[20];    // 报名时间,用char对应Oracle的date,格式:'yyyy-mm-dd hh24:mi:ss'。
char memo[301];    // 备注,用char[301]对应Oracle的varchar2(300)。

1.静态sql语句效率不如动态sql语句(采用bind绑定)效率高。原因是静态sql语句每次都需要准备sql语句(stmt.prepare(...)),底层是tcp通信,因此效率不高。

2.单引号中想用单引号需要用两个单引号。

stmt.prepare("\
    insert into girls(id,name,weight,btime,memo) \
        values(%d,'西施%05dgirl',%.1f,to_date('2000-01-01 12:30:%02d','yyyy-mm-dd hh24:mi:ss'),'这是''第%05d个女生的备注。')",ii,ii,45.35+ii,ii,ii);

3.sqlstatement类的bindin方法

① 字符类型推荐用char[],不推荐string,但可以用。

char[]:如果字段是字符串型,bindin()的第三个参数填字段的长度,填大一些没关系。太小可能会有问题,不推荐缺省值2000。

string:

1)如果SQL语句没有改变,只需要bindin一次就可以了;

2)如果value的类型是string,bindin()函数中会resize(len);

3)如果value的类型是string,那么,在用户的程序代码中,不可改变它内部buffer的地址

② 绑定的变量用结构体看起来更舒服。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值