public:
OCIEnv
*envhp;//
环境句柄
OCIServer
*srvhp;//
服务器句柄
OCISvcCtx
*svchp;//
服务环境句柄
OCIError
*errhp;//
错误句柄
OCISession
*authp;//
会话句柄
OCIStmt
*stmthp;//
语句句柄
OCIDescribe
*dschp;//
描述句柄
其次
,
在本工程的对话框资源中加入一个
Button
控件
(
序号
1
,详细配置见表
4
,以下加入的控件的配置情况都在表
4
中
),
同时利用
ClassWizard
为该按钮添加一个“
BN_CLICKED
”函数――
OnButConnectdb(),
在这个函数中我们实现
OCI
程序的前三步,即:
创建
OCI
环境,分配句柄与数据结构,连接数据库与开始会话。并且
列举数据库中某个用户的所有基表。
第三
,
我们在对话框中加入一个
List Box
控件
(
序号
2),
并且利用
ClassWizard
为该控件添加两个变量
: m_listTablename(
类型为
ClistBox)
与
m_strTablename (
类型为
Cstring)
。我们在该控件中显示所连接用户的所有基表(
table base
)。
我们在
OnButConnectdb()
中添加创建
OCI
环境的代码
4.1.2
。
//
代码
4.1.2
,初始化
OCI
环境
OCIEnvInit(&envhp,OCI_DEFAULT,0, 0 );
我们在类
COCIExampleDlg
中加入一个报告
OCI
错误的函数
ErrorProc
(),代码如下:
//
代码
4.1.3
ErrorProc(dvoid *err, sword status)
{
Cstring str;sb4 errcode;text errbuf[512];
if(status==OCI_ERROR)
{
//
调用
OCI
的错误获取函数
OCIErrorGet((dvoid*)errhp,(ub4)1,NULL,
&errcode,errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR);
str.Format(”
错误号
:%d/n
错误信息
:%s/n”,
errcode,errbuf);
AfxMessageBox(str);
}}
4.2
分配句柄与数据结构
在函数
OnButConnectdb()
中加入如
4.2.1
代码来分配
OCI
的句柄与描述符。
//
代码
4.2.1
:分配句柄与数据结构
//
分配错误句柄
OCIHandleAlloc(envhp,(void**)&errhp,
OCI_HTYPE_ERROR,0,0);
//
分配服务器句柄
OCIHandleAlloc(envhp,(void**)&srvhp,
OCI_HTYPE_SERVER,0,0);
//
分配服务环境句柄
OCIHandleAlloc(envhp,(void**)&svchp,
OCI_HTYPE_SVCCTX,0,0);
//
分配会话句柄
OCIHandleAlloc(envhp,(void **)&authp,
OCI_HTYPE_SESSION,0,0);
//
分配描述句柄
OCIHandleAlloc((dvoid *) envhp,
(dvoid **) &dschp, OCI_HTYPE_DESCRIBE,0,0);
//
分配语句句柄
OCIHandleAlloc((dvoid *) envhp,
(dvoid**)&stmthp, OCI_HTYPE_STMT,
0, 0));
说明:常量 “
OCI_HTYPE_STMT
”等等是
OCI
句柄的宏定义,标明我们所分配的句柄的类型。句柄的宏定义具体可以参考
oci.h
文件中的“
Handle Types”
部分。
4.3
连接数据库与开始会话
由于在连接数据库与开始用户会话中需要数据库实例的名称、用户名称以及用户密码,因此我们在这个工程中加入一个对话框类来接收这些信息的输入。在
OCIExample
工程中加入基于
CDialog
类的对话框类――
CCconnectDlg
类。
加入
CcconnectDlg
类的步骤:首先,在
VC
的“
WorkSpace/ResourceView”
中选中“
Dialog
”,并且右击鼠标后,弹出上下文菜单,选中“
Insert Dialog
”,这样就给本工程中加入了一个对话框资源;其次,选中刚才加入的对话框,右击鼠标,选中上下文菜单中的“
ClassWizard
”,选择“
Create new Class
”,输入类的名称――
CCconnectDlg
;第三,在刚加入的对话框上(
CcconnectDlg
)加入如表
2
的控件。第四,给其中的三个
CEdit
控件加入用来接收用户输入信息的
CString
类型的变量,如表
1
。这样就在本工程中加入了一个可以接收输入用户信息的对话框类。
N
|
对象
|
ID
|
Caption
或变量
|
1
|
Static Text
|
IDC_STATIC
|
OracleDB
|
2
|
CEdit
|
IDC_DBNAME
|
m_strDBName
|
3
|
Static Text
|
IDC_STATIC
|
UserName
|
4
|
CEdit
|
IDC_UNAME
|
m_strUserName
|
5
|
Static Text
|
IDC_STATIC
|
UserID
|
6
|
CEdit
|
IDC_UID
|
m_strUserID
|
表
2
接着我们在函数
OnButConnectdb()
中加入如
4.3.1
代码,来接收用户输入信息。
//
代码
4.3.1
,生成接收用户输入数据的对话框
CConectDlg
ConnDlg;
ConnDlg.m_strDBName=”MyOracleDB”;
ConnDlg.m_strUserName=”scott”;
ConnDlg.m_strUserID=”tiger”;
if(ConnDlg.DoModal()==IDCANCEL)return;
下面我们就可以利用用户输入的登录信息来连接数据库并且开始会话。
在函数
OnButConnectdb()
中加入连接数据库代码。
//
代码
4.3.2
,连接数据库
UpdateData(TRUE);sword status;
status=OCIServerAttach(srvhp,errhp,
(unsignedchar*)(LPCTSTR)ConnDlg.m_strDBName,
(sb4)strlen(ConnDlg.m_strDBName),OCI_DEFAU;
//
设置数据库的属性:服务器环境
ErrorProc(errhp,OCIAttrSet((dvoid *) svchp,
(ub4) OCI_HTYPE_SVCCTX,
(dvoid *) srvhp, (ub4) 0,
OCI_ATTR_SERVER, errhp));
//
代码
4.3.3
,认证用户并且开始会话
LPCTSTR uid, pwd;
uid=ConnDlg.m_strUserName;
pwd=ConnDlg.m_strUserID;
//
认证用户名称
;
ErrorProc(errhp,OCIAttrSet(authp,
OCI_HTYPE_SESSION, (dvoid *)uid,
(ub4)strlen(uid),OCI_ATTR_USERNAME,errhp));
//
认证用户密码
ErrorProc(errhp,OCIAttrSet(authp,
OCI_HTYPE_SESSION, (dvoid *)pwd,
(ub4)strlen(pwd),OCI_ATTR_PASSWORD,errhp));
//
以申明的用户名称与密码开始会话
status=OCISessionBegin(svchp, errhp,
authp,OCI_CRED_RDBMS,OCI_DEFAULT);
if(status!=0) return ;
//
设置服务器环境
status=OCIAttrSet(svchp,OCI_HTYPE_SVCCTX,
(dvoid*)authp,0,OCI_ATTR_SESSION,errhp);
if(status!=0) return;
4.4
执行
SQL
这一部分比较如复杂。在
OCI
环境中执行
SQL
的过程在第二部分的
3
中已经分析过了。我们首先做一个没有条件的
SELECT
类型的
SQL
语句中
OCI
中的执行过程,而且按照第二部分的
3
中的步骤进行。在
在函数
OnButConnectdb()
中加入如下几段代码,其目的就是把我们所连接的数据库的某个用户的所有基表都读取到应用程序中,并且把基表的名称显示到到我们在
4.1
中加入的
ListBox
控件中。
首先,要在本过程的类
COCIExampleDlg
中加入一些
public
类型的变量与
OCI
的句柄。如代码
4.4.1
代码
4.4.1
OCIDefine *defhp[20];//
定义句柄
OCIBind
*bidhp[20];//
绑定句柄
OCIParam *colhp;
//
列句柄
ub2 collen[30]; //
列长度
ub2 coltype[30];//
列类型
//
存放
SELECT
语句选中的列数据
text* colbuf[30];
sb2 ind[30];//
指示符变量
我们注意到,这里有诸如
ub2
、
text
等变量类型,这是
OCI
的头文件
oci.h
中定义的一些适合
OCI
的
C
语言变量类型,其中常用类型与
C
语言的对应关系如表
3
:更详细的资料可以参考
oci.h
文件的宏定义部分。
OCI
类型
|
C
语言类型
|
OCI
类型
|
C
语言类型
|
Text
|
unsigned char
|
size_t
|
unsigned int
|
sword
|
signed int
|
sb4
|
signed int
|
Ub1
|
unsigned char
|
sb2
|
signed char
|
Ub 2
|
unsigned short
|
sb1
|
signed char
|
Ub 4
|
unsigned int
|
eb4
|
int
|
表
3
//
代码
4.4.2,
处理
SQL
的第一步,准备
SQL
CString strSQL; text textSQL[1024];
strSQL=”SELECT TABLE_NAME FROM USER_TABLES”;
wsprintf((char*)textSQL,”%s”,strSQL);
status=OCIStmtPrepare(stmthp,errhp,
textSQL,strlen((char*)textSQL),
OCI_NTV_SYNTAX,OCI_DEFAULT );
由于我们所要处理的这个
SELECT
语句没有选择条件,即
SQL
语句中没有输入数据。因此我们就不需要处理
SQL
的第二步“绑定”;
//
代码
4.4.3
,处理
SQL
的第三步:执行
ErrorProc(errhp,OCIStmtExecute(svchp,
stmthp,errhp,(ub4)0,0,
NULL,NULL, OCI_DEFAULT))
//
代码
4.4.4,
处理
SQL
的第四步,描述
ub4 col_num;//
存放
SELECT
语句选中的列数
//
为选择项分配参数描述符
ErrorProc(errhp,OCIParamGet(stmthp,
OCI_HTYPE_STMT,errhp,
(void **)&colhp,1));
//
读取选择项的数据长度
ErrorProc(errhp,OCIAttrGet(colhp,
OCI_DTYPE_PARAM,&collen[0],0,
OCI_ATTR_DATA_SIZE,errhp));
//
读取选择项的数据类型
ErrorProc(errhp,OCIAttrGet(colhp,
OCI_DTYPE_PARAM,&coltype[0],0,
OCI_ATTR_DATA_TYPE,errhp));
//
分配缓冲区
colbuf[0]=(text*)new text[(int)collen[0]+1];
//
代码
4.4.5
处理
SQL
的第五步,定义变量
ErrorProc(errhp,OCIDefineByPos(stmthp,
&defhp[k-1], errhp, 1,
(ub1*)colbuf[0],collen[0]+1,
SQLT_STR,&
ind
[0],0,0,OCI_DEFAULT));
//
代码
4.4.6,
处理
SQL
的第六步,取值
while((OCIStmtFetch(stmthp,errhp,1,
OCI_FETCH_NEXT,OCI_DEFAULT))!=OCI_NO_DATA)
{
//
把获取的用户的基表的名称加到
ListBox
中
m_listTablename.AddString((char*)
colbuf[0]);
}
delete
colbuf[0]; //
删除所分配的缓冲区
4.5
结束会话、断开连接
以及释放句柄
以上我们就已经做了一个
OCI
应用程序六步骤的
前三步,即创建
OCI
环境、分配句柄与数据结构、连接数据库与开始会话以及执行
SQL
与处理数据,那么,一个完整的
OCI
应用程序还需要两个步骤,即
结束会话与断开连接与释放句柄,我们可以把这两部分放在
COCIExampleDlg
类的析构函数中,或者类
COCIExampleDlg
的虚函数
DestroyWindow()
中,代码如下:
//
代码
4.5.1
,
//
结束会话
OCISessionEnd(svchp, errhp, authp, (ub4) 0);
//
断开与数据库的连接
OCIServerDetach(srvhp,errhp,OCI_DEFAULT);
//
释放
OCI
句柄
OCIHandleFree((dvoid*)srvhp,
OCI_HTYPE_SERVER);
OCIHandleFree((dvoid*)svchp,
OCI_HTYPE_SVCCTX);
OCIHandleFree((dvoid*)errhp,
OCI_HTYPE_ERROR);
OCIHandleFree((dvoid*)authp,
OCI_HTYPE_SESSION);
OCIHandleFree((dvoid*)stmthp,
OCI_HTYPE_STMT);
OCIHandleFree((dvoid*)dschp,
OCI_HTYPE_DESCRIBE);
4.6
小结
从
4.1
到
4.4
就是在一个应用程序中应用
OCI
的完整步骤。但是仅仅在
4.3
中演示了不带条件的
DQL
语句在
OCI
中的应用方法,还没有涉及到其它类型的
SQL
语句。因此,下面的内容是进一步演示各种类型的
SQL
语句在
OCI
中的实现过程。
5.
在
ListCtrl
控件中显示表的数据
我们先在对话框上添加一个
Button
控件
(
序号
3)
与一个
List Control(
序号
4)(
具体配置见表
),
并且加入
control
类型的变量
m_listCtrl
。然后利用
ClassWizard
为
Button
控件添加一个“
BN_CLICKED
”
函数――
OnBTableselectok()
。我们利用这个函数实现从控件
2
选中的表里读出字段名以及数据并且把它们显示到控件
4
上。同时,我们在类
COCIExampleDlg
中加入如下的
public
类型的变量:
//
代码
5
//
存储表的字段名称
CString ColName[50]; //
存储表的字段名称
CString ColType[50];//
存储字段的的数据类型
CString ColVal[50][100]; //
存储表的字段值
int ColumnNumbers; //
字段的数目
CString TableName; //
所选中表的名称
在函数
OnBTableselectok()
中添加代码如下
:
//
代码
5.1,
获取控件
2
所选中的表名
TableName=””;
int item=m_listTablename.GetCurSel();
m_listTablename.GetText(item,TableName);
if(TableName==””) return;
//
代码
5.2,
处理
SQL
的第一步,准备
SQL
text textSQL[1024]; sword status;
wsprintf((char*)textSQL,
“SELECT * FROM %s”,TableName);
if(status=OCIStmtPrepare(stmthp,errhp,
textSQL,strlen((char*)textSQL),
OCI_NTV_SYNTAX,OCI_DEFAULT ))
{ErrorProc(errhp,status); return;}
//
代码
5.3,
处理
SQL
的第三步,执行
if(status=OCIStmtExecute(svchp,stmthp,
errhp,(ub4)0,0,NULL,NULL,OCI_DEFAULT))
{ ErrorProc(errhp,status); return; }
//
代码
5.4,
处理
SQL
的第四步,描述
ub4 col_num;//
存放
SELECT
语句选中的列数
//
代码
5.4.1,
读取选择列表中的项数
ErrorProc(errhp,OCIAttrGet(stmthp,
OCI_HTYPE_STMT,&col_num,0,
OCI_ATTR_PARAM_COUNT,errhp));
ColumnNumbers=(int) col_num;
text
*namep; //
字段名称
ub4
sizep; //
字段名称的字符串长度
text tempText[100];
//
获取表的字段的属性信息
for(int i=0;i<(int)col_num;i++)
{
//
为选择项分配参数描述符
ErrorProc(errhp,OCIParamGet(stmthp,
OCI_HTYPE_STMT,errhp,
(void **)&colhp,ub4(i+1)));
//
读取选择项的数据长度
ErrorProc(errhp,OCIAttrGet(colhp,
OCI_DTYPE_PARAM,&collen[i],0,
OCI_ATTR_DATA_SIZE,errhp));
//
读取选择项的数据类型
ErrorProc(errhp,OCIAttrGet(colhp,
OCI_DTYPE_PARAM,&coltype[i],0,
OCI_ATTR_DATA_TYPE,errhp));
//
若这个字段为日期型,则把其字符宽度置为
30
if(coltype[i]==SQLT_DAT) collen[i]=30;
//
分配缓冲区
colbuf[i]=(text*)new text[(int)collen[i]+1];
//
代码
5.4.2
获取字段名称
ErrorProc(errhp,OCIAttrGet(colhp,
OCI_DTYPE_PARAM, (dvoid*)&namep,
(ub4*)&sizep,OCI_ATTR_NAME, errhp));
strncpy((char *)tempText,
(char *)namep, (size_t) sizep);
tempText[sizep] = ‘/0’;
//
把字段的名称赋予字段名称数组
ColName[i].Format(“%s”,tempText);
}
//
代码
5.5,
处理
SQL
的第五步,定义变量
for(i=0;i<(int)col_num;i++)
{
if(status=OCIDefineByPos(stmthp,&defhp[i],
errhp,i+1,(ub1*)colbuf[i], collen[i]+1,
SQLT_STR,&
ind
[i],0,0, OCI_DEFAULT))
{ ErrorProc(errhp,status); return; }
}
//
代码
5.6
,处理
SQL
的第六步,取值
int row=0;
while((OCIStmtFetch(stmthp,errhp,1,OCI_FETCH_NEXT,OCI_DEFAULT))!=OCI_NO_DATA)
{
for(i=0;i<(int)col_num;i++)
{//
把获取的用户的基表的数据
//
赋予字段数值数组
ColVal[row][i]=colbuf[i];
}
row=row+1;
}
//
删除所分配的缓冲区
for(i=0;i<(int)col_num;i++)delete colbuf[i];
//
代码
5.7,
把获取的数据显示到
ListCtrl
上
//
设置
ListCtrl
的样式
m_listCtrl.ModifyStyle(NULL,
LVS_REPORT);
m_listCtrl.SetExtendedStyle(LVS_EX_FLATSB | LVS_EX_FULLROWSELECT
|LVS_EX_GRIDLINES);
m_listCtrl.SetFocus();
//
加
ListCtrl
数据列的标题
int ColNumber=
m_listCtrl.GetHeaderCtrl()->GetItemCount();
//
清空
for(i=0;i<ColNumber;i++)
m_listCtrl.DeleteColumn(0);
|