PowerDesign9.5+ 中的GTL编程 解决大问题数据库教程(精选9篇)由网友“礼68329”投稿提供,下面是小编帮大家整理后的PowerDesign9.5+ 中的GTL编程 解决大问题数据库教程,希望对大家有所帮助。
篇1:PowerDesign9.5+ 中的GTL编程 解决大问题数据库教程
编程|解决|问题
在开发数据库模型时,通常会有这样的需求
输入一个 表或字段的中文名称 自动生成它的中文拼音首字母简写功能
如 :
设备_变压器 自动形成数据库表名 SB_BYQ
在 POWERDesigner 9.5 以上版本中可以这样设置
打开 1,Tools >ModelOptions..对话框
2。点击左边 Name Convension 树形结点
3。在右边面板中选择 Name/Code Convensions 选择框
4。在 Name/Code 页中 Convension Script. 输入框中输入下面的代码
.foreach_part(%Name%,“.”)
.vbscript(%CurrentPart%)
ScriptResult=getpy(ScriptInputArray(0))
function getpychar(char)
tmp=65536+asc(char)
if(tmp>=45217 and tmp<=45252) then
getpychar= “A”
elseif(tmp>=45253 and tmp<=45760) then
getpychar= “B”
elseif(tmp>=45761 and tmp<=46317) then
getpychar= “C”
elseif(tmp>=46318 and tmp<=46825) then
getpychar= “D”
elseif(tmp>=46826 and tmp<=47009) then
getpychar= “E”
elseif(tmp>=47010 and tmp<=47296) then
getpychar= “F”
elseif(tmp>=47297 and tmp<=47613) then
getpychar= “G”
elseif(tmp>=47614 and tmp<=48118) then
getpychar= “H”
elseif(tmp>=48119 and tmp<=49061) then
getpychar= “J”
elseif(tmp>=49062 and tmp<=49323) then
getpychar= “K”
elseif(tmp>=49324 and tmp<=49895) then
getpychar= “L”
elseif(tmp>=49896 and tmp<=50370) then
getpychar= “M”
elseif(tmp>=50371 and tmp<=50613) then
getpychar= “N”
elseif(tmp>=50614 and tmp<=50621) then
getpychar= “O”
elseif(tmp>=50622 and tmp<=50905) then
getpychar= “P”
elseif(tmp>=50906 and tmp<=51386) then
getpychar= “Q”
elseif(tmp>=51387 and tmp<=51445) then
getpychar= “R”
elseif(tmp>=51446 and tmp<=52217) then
getpychar= “S”
elseif(tmp>=52218 and tmp<=52697) then
getpychar= “T”
elseif(tmp>=52698 and tmp<=52979) then
getpychar= “W”
elseif(tmp>=52980 and tmp<=53640) then
getpychar= “X”
elseif(tmp>=53689 and tmp<=54480) then
getpychar= “Y”
elseif(tmp>=54481 and tmp<=62289) then
getpychar= “Z”
else '如果不是中文,则不处理
getpychar=char
end if
end function
function getpy(str)
for i=1 to len(str)
getpy=getpy&getpychar(mid(str,i,1))
next
end function
.endvbscript
篇2:ADO 编程模型详细资料数据库教程
ado|编程
ADO 编程模型详细资料以下元素是 ADO 编程模型中的关键部分:
连接
命令
参数
记录集
字段
错误
属性
集合
事件
连接
通过“连接”可从应用程序访问数据源,连接是交换数据所必需的环境,通过如 Microsoft® Internet Information Server 作为媒介,应用程序可直接(有时称为双层系统)或间接(有时称为三层系统)访问数据源。
对象模型使用 Connection 对象使连接概念得以具体化。
“事务”用于界定在连接过程中发生的一系列数据访问操作的开始和结束。ADO 可明确事务中的操作造成的对数据源的更改或者成功发生,或者根本没有发生。
如果取消事务或它的一个操作失败,则最终的结果将仿佛是事务中的操作均未发生,数据源将会保持事务开始以前的状态。
对象模型无法清楚地体现出事务的概念,而是用一组 Connection 对象方法来表示。
ADO 访问来自 OLE DB 提供者的数据和服务。Connection 对象用于指定专门的提供者和任意参数。例如,可对远程数据服务 (RDS) 进行显式调用,或通过“Microsoft OLE DB Remoting Provider”进行隐式调用。(请参阅 RDS 教程通过“MS Remote Provider”调用 RDS 第二步的范例)
命令
通过已建立的连接发出的“命令”可以某种方式来操作数据源。一般情况下,命令可以在数据源中添加、删除或更新数据,或者在表中以行的格式检索数据。
对象模型用 Command 对象来体现命令概念。Command 对象使 ADO 能够优化对命令的执行。
参数
通常,命令需要的变量部分即“参数”可以在命令发布之前进行更改。例如,可重复发出相同的数据检索命令,但每一次均可更改指定的检索信息。
参数对执行其行为类似函数的命令非常有用,这样就可知道命令是做什么的,但不必知道它如何工作。例如,可发出一项银行过户命令,从一方借出贷给另一方。可将要过户的款额设置为参数。
对象模型用 Parameter 对象来体现参数概念。
记录集
如果命令是在表中按信息行返回数据的查询(行返回查询),则这些行将会存储在本地。
对象模型将该存储体现为 Recordset 对象。但是,不存在仅代表单独一个 Recordset 行的对象。
记录集是在行中检查和修改数据最主要的方法。Recordset 对象用于: 指定可以检查的行。
移动行。
指定移动行的顺序。
添加、更改或删除行。
通过更改行更新数据源。
管理 Recordset 的总体状态,
字段
一个记录集行包含一个或多个“字段”。如果将记录集看作二维网格,字段将排列构成“列”。每一字段(列)都分别包含有名称、数据类型和值的属性,正是在该值中包含了来自数据源的真实数据。
对象模型以 Field 对象体现字段。
要修改数据源中的数据,可在记录集行中修改 Field 对象的值,对记录集的更改最终被传送给数据源。作为选项,Connection 对象的事务管理方法能够可靠地保证更改要么全部成功,要么全部失败。
错误
错误随时可在应用程序中发生,通常是由于无法建立连接、执行命令或对某些状态(例如,试图使用没有初始化的记录集)的对象进行操作。
对象模型以 Error 对象体现错误。
任意给定的错误都会产生一个或多个 Error 对象,随后产生的错误将会放弃先前的 Error 对象组。
属性
每个 ADO 对象都有一组唯一的“属性”来描述或控制对象的行为。
属性有两种类型:内置和动态。内置属性是 ADO 对象的一部分并且随时可用。动态属性则由特别的数据提供者添加到 ADO 对象的属性集合中,仅在提供者被使用时才能存在。
对象模型以 Property 对象体现属性。
集合
ADO 提供“集合”,这是一种可方便地包含其他特殊类型对象的对象类型。使用集合方法可按名称(文本字符串)或序号(整型数)对集合中的对象进行检索。
ADO 提供四种类型的集合: Connection 对象具有 Errors 集合,包含为响应与数据源有关的单一错误而创建的所有 Error 对象。
Command 对象具有 Parameters 集合,包含应用于 Command 对象的所有 Parameter 对象。
Recordset 对象具有 Fields 集合,包含所有定义 Recordset 对象列的 Field 对象。
另外,Connection、Command、Recordset 和 Field 对象都具有 Properties 集合。它包含所有属于各个包含对象的 Property 对象。
ADO 对象拥有可在其上使用的诸如“整型”、“字符型”或“布尔型”这样的普通数据类型来设置或检索值的属性。然而,有必要将某些属性看成是数据类型“COLLECTION OBJECT”的返回值。相应的,集合对象具有存储和检索适合该集合的其他对象的方法。
例如,可认为 Recordset 对象具有能够返回集合对象的 Properties 属性。该集合对象具有存储和检索描述 Recordset 性质的 Property 对象的方法。
事件
“事件”是对将要发生或已经发生的某些操作的通知。一般情况下,可用事件高效地编写包含几个异步任务的应用程序。
对象模型无法显式体现事件,只能在调用事件处理程序例程时表现出来。
在操作开始之前调用的事件处理程序便于对操作参数进行检查或修改,然后取消或允许操作完成。
操作完成后调用的事件处理程序在异步操作完成后进行通知。多个操作经过增强可以有选择地异步执行。例如,用于启动异步 Recordset.Open 操作的应用程序将在操作结束时得到执行完成事件的通知。 有关事件的详细信息,请参阅 ADO 事件模型和异步操作。
篇3:Java数据库编程中的几个常用技巧
1、java数据库操作基本流程
2、几个常用的重要技巧:
可滚动、更新的记录集
批量更新
事务处理
java数据库操作基本流程:取得数据库连接 - 执行sql语句 - 处理执行结果 - 释放数据库连接
1、取得数据库连接
1)用DriverManager取数据库连接
例子:
String className,url,uid,pwd;
className = “oracle.jdbc.driver.OracleDriver”;
url = “jdbc:oracle:thin:@127.0.0.1:1521:orasvr;
uid = ”system“;
pwd = ”manager“;
Class.forName(className);
Connection cn = DriverManager.getConnection(url,uid,pwd);
2)用jndi(java的命名和目录服务)方式
例子
String jndi = ”jdbc/db“;
Context ctx = (Context) new InitialContext.lookup(”java:comp/env“);
DataSource ds = (DataSource) ctx.lookup(jndi);
Connection cn = ds.getConnection();
多用于jsp中
2、执行sql语句
1)用Statement来执行sql语句
String sql;
Statement sm = cn.createStatement();
sm.executeQuery(sql); // 执行数据查询语句(select)
sm.executeUpdate(sql); // 执行数据更新语句(、update、、drop等)statement.close();
2)用PreparedStatement来执行sql语句
String sql;
sql = ” into user (id,name) values (?,?)“;
PreparedStatement ps = cn.prepareStatement(sql);
ps.setInt(1,xxx);
ps.setString(2,xxx);
...
ResultSet rs = ps.executeQuery(); // 查询
int c = ps.executeUpdate(); // 更新
3、处理执行结果
查询语句,返回记录集ResultSet。
更新语句,返回数字,表示该更新影响的记录数。
ResultSet的方法:
1、next(),将游标往后移动一行,如果成功返回true;否则返回false。
2、getInt(”id“)或getSting(”name“),返回当前游标下某个字段的值。
3、释放连接。
cn.close();
一般,先关闭ResultSet,然后关闭Statement(或者PreparedStatement);最后关闭Connection
可滚动、更新的记录集
1、创建可滚动、更新的Statement
Statement sm = cn.createStatement(ResultSet.TYPE_SCROLL_ENSITIVE,ResultSet.CONCUR_READ_ONLY);
该Statement取得的ResultSet就是可滚动的
2、创建PreparedStatement时指定参数
PreparedStatemet ps = cn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet.absolute(9000);
批量更新
1、Statement
Statement sm = cn.createStatement();
sm.addBatch(sql1);
sm.addBatch(sql2);
...
sm.executeBatch()
一个Statement对象,可以执行多个sql语句以后,批量更新。这多个语句可以是、update、等或兼有
2、PreparedStatement
PreparedStatement ps = cn.preparedStatement(sql);
{
ps.setXXX(1,xxx);
...
ps.addBatch();
}
ps.executeBatch();
一个PreparedStatement,可以把一个sql语句,变换参数多次执行,一次更新。
事务的处理
1、关闭Connection的自动提交
cn.setAutoCommit(false);
2、执行一系列sql语句
要点:执行每一个新的sql语句前,上一次执行sql语句的Statement(或者PreparedStatemet)必须先close
Statement sm ;
sm = cn.createStatement( into user...);
sm.executeUpdate();
sm.close();
sm = cn.createStatement(” into corp...);
sm.executeUpdate();
sm.close();
3、提交
cn.commit();
4、如果发生异常,那么回滚
cn.rollback();
[Java数据库编程中的几个常用技巧]
篇4:Library Cache Lock的解决数据库教程
cache|解决
昨晚业务系统导入资料并重建索引时一个会话突然停滞不前,用TOAD一看,一直在等待Library Cache Lock,Library Cache Lock的解决数据库教程
。TOAD、OEM中都看不到此锁,会话每三秒启动一次,但每次都是等待这个锁。显然,这和数据字典有关,应该是一个索引的数据字典中的记录被锁住了,导致无法重建。可是杀光了其他ACTIVE的会话,问题仍然没有得到解决,看来是某一个被杀死的会话持有该锁,而会话尚未回滚完全,进程仍然吊死着。现在的问题就是找这个会话了。首先想到的文档就是Oracle9i Database Reference了,找到附录A,说明如下:
This event controls the concurrency between clients of the library cache. It acquires a lock on the object handle so that either:
One client can prevent other clients from accessing the same object
The client can maintain a dependency for a long time (for example, no other client can change the object)
This lock is also obtained to locate an object in the library cache.
Wait Time: 3 seconds (1 second for PMON)
Parameters:
handle address
Address of the object being loaded
lock address
Address of the load lock being used. This is not the same thing as a latch or an enqueue, it is a State Object.
mode
Indicates the data pieces of the object which need to be loaded
namespace
See “namespace”
几乎等于什么都没说,不过lock address应该会有点用处。
转而上网搜索解决方案,终于找到一篇metalink上的文档:
Doc ID:
Note:122793.1
Subject: HOW TO FIND THE SESSION HOLDING A LIBRARY CACHE LOCK
Type: BULLETIN
Status: PUBLISHED
Content Type: TEXT/PLAIN
Creation Date: 23-OCT-
Last Revision Date: 17-JUL-
PURPOSE
-------
In some situations it may happen your session is 'hanging' and is awaiting for
a 'Library cache lock'. This document describes how to find the session that
in fact has the lock you are waiting for.
SCOPE & APPLICATION
-------------------
Support analysts, dba's, ..
HOW TO FIND THE SESSION HOLDING A A LIBRARY CACHE LOCK
------------------------------------------------------
Common situations:
* a DML operation that is hanging because the table which is accessed is currently
undergoing changes (ALTER TABLE). This may take quite a long time depending on
the size of the table and the type of the modification
(e.g. ALTER TABLE x MODIFY (col1 CHAR(200) on thousands of records).
* The compilation of package will hang on Library Cache Lock and Library Cache Pin
if some users are executing any Procedure/Function defined in the same package.
In the first situation the V$LOCK view will show that the session doing the
'ALTER TABLE' has an exclusive DML enqueue lock on the table object (LMODE=6,
TYPE=TM and ID1 is the OBJECT_ID of the table). The waiting session however does
not show up in V$LOCK yet so in an environment with a lot of concurrent sessions
the V$LOCK information is insufficient to track down the culprit blocking your
operation.
METHOD 1: SYSTEMSTATE ANALYSIS
------------------------------
One way of finding the session blocking you is to analyze the system state dump.
Using the systemstate event one can create a tracefile containing detailed
information on every Oracle process. This information includes all the resources
held & requested by a specific process.
Whilst an operation is hanging, open a new session and launch the following
statement:
ALTER SESSION SET EVENTS 'IMMEDIATE TRACE NAME SYSTEMSTATE LEVEL 8';
Oracle will now create a systemstate tracefile in your USER_DUMP_DEST directory.
Get the PID (ProcessID) of the 'hanging' session from the V$PROCESS by matching
PADDR from V$SESSION with ADDR from V$PROCESS:
SELECT PID FROM V$PROCESS WHERE ADDR=
(SELECT PADDR FROM V$SESSION WHERE SID=sid_of_hanging_session);
The systemstate dump contains a separate section with information for each
process. Open the tracefile and do a search for 'PROCESS pid_from_select_stmt'.
In the process section look up the wait event by doing a search on 'waiting for'.
Example output:
PROCESS 8:
----------------------------------------
SO: 50050b08, type: 1, owner: 0, flag: INIT/-/-/0x00
(process) Oracle pid=8, calls cur/top: 5007bf6c/5007bf6c, flag: (0) -
int error: 0, call error: 0, sess error: 0, txn error 0
(post info) last post received: 82 0 4
last post received-location: kslpsr
last process to post me: 5004ff08 1 2
last post sent: 0 0 13
last post sent-location: ksasnd
last process posted by me: 5004ff08 1 2
(latch info) wait_event=0 bits=0
Process Group: DEFAULT, pseudo proc: 50058ac4
O/S info: user: daemon, term: pts/1, ospid: 15161
OSD pid info: 15161
----------------------------------------
SO: 5005f294, type: 3, owner: 50050b08, flag: INIT/-/-/0x00
(session) trans: 0, creator: 50050b08, flag: (41) USR/- BSY/-/-/-/-/-
DID: 0001-0008-00000002, short-term DID: 0000-0000-00000000
txn branch: 0
oct: 6, prv: 0, user: 41/LC
O/S info: user: daemon, term: pts/1, ospid: 15160, machine: goblin.forgotten.realms
program: sqlplus@goblin.forgotten.realms (TNS V1-V3)
application name: SQL*Plus, hash value=3669949024
waiting for 'library cache lock' blocking sess=0x0 seq=253 wait_time=0
!>> handle address=5023ef9c, lock address=5019cad4, 10*mode+namespace=15
Using the 'handle address' you can look up the process that is keeping a lock
on your resource by doing a search on the address within the same tracefile.
Example output:
PROCESS 9:
----------------------------------------
SO: 50050e08, type: 1, owner: 0, flag: INIT/-/-/0x00
(process) Oracle pid=9, calls cur/top: 5007bbac/5007bbfc, flag: (0) -
int error: 0, call error: 0, sess error: 0, txn error 0
----------------------------------------
SO: 5019d5e4, type: 34, owner: 5015f65c, flag: INIT/-/-/0x00
!>> LIBRARY OBJECT PIN: pin=5019d5e4 handle=5023ef9c mode=X lock=0
user=5005fad4 session=5005fad4 count=1 mask=0511 savepoint=118218 flags=[00]
From the output we can see that the Oracle process with PID 9 has an exclusive
lock on the object we are trying to access. Using V$PROCESS and V$SESSION we can
retrieve the sid,user,terminal,program,... for this process. The actual statement
that was launched by this session is also listed in the tracefile (statements and
other library cache objects are preceded by 'name=').
METHOD 2: EXAMINE THE X$KGLLK TABLE
-----------------------------------
The X$KGLLK table (accessible only as SYS/INTERNAL) contains all the
library object locks (both held & requested) for all sessions and
is more complete than the V$LOCK view although the column names don't
always reveal their meaning.
You can examine the locks requested (and held) by the waiting session
by looking up the session address (SADDR) in V$SESSION and doing the
following select:
select * from x$kgllk where KGLLKSES = 'saddr_from_v$session'
This will show you all the library locks held by this session where
KGLNAOBJ contains the first 80 characters of the name of the object.
The value in KGLLKHDL corresponds with the 'handle address' of the
object in METHOD 1.
You will see that at least one lock for the session has KGLLKREQ >0
which means this is a REQUEST for a lock (thus, the session is waiting).
If we now match the KGLLKHDL with the handles of other sessions in
X$KGLLK that should give us the address of the blocking session since
KGLLKREQ=0 for this session, meaning it HAS the lock.
SELECT * FROM X$KGLLK LOCK_A
WHERE KGLLKREQ = 0
AND EXISTS (SELECT LOCK_B.KGLLKHDL FROM X$KGLLK LOCK_B
WHERE KGLLKSES = 'saddr_from_v$session' /* BLOCKED SESSION */
AND LOCK_A.KGLLKHDL = LOCK_B.KGLLKHDL
AND KGLLKREQ >0);
If we look a bit further we can then again match KGLLKSES with SADDR
in v$session to find further information on the blocking session:
SELECT SID,USERNAME,TERMINAL,PROGRAM FROM V$SESSION
WHERE SADDR in
(SELECT KGLLKSES FROM X$KGLLK LOCK_A
WHERE KGLLKREQ = 0
AND EXISTS (SELECT LOCK_B.KGLLKHDL FROM X$KGLLK LOCK_B
WHERE KGLLKSES = 'saddr_from_v$session' /* BLOCKED SESSION */
AND LOCK_A.KGLLKHDL = LOCK_B.KGLLKHDL
AND KGLLKREQ >0)
);
In the same way we can also find all the blocked sessions:
SELECT SID,USERNAME,TERMINAL,PROGRAM FROM V$SESSION
WHERE SADDR in
(SELECT KGLLKSES FROM X$KGLLK LOCK_A
WHERE KGLLKREQ >0
AND EXISTS (SELECT LOCK_B.KGLLKHDL FROM X$KGLLK LOCK_B
WHERE KGLLKSES = 'saddr_from_v$session' /* BLOCKING SESSION */
AND LOCK_A.KGLLKHDL = LOCK_B.KGLLKHDL
AND KGLLKREQ = 0)
);
RELATED DOCUMENTS
-----------------
[NOTE:1020008.6] SCRIPT. FULLY DECODED LOCKING SCRIPT
[NOTE:1054939.6] COMPILATION OF PACKAGE IS HANGING ON LIBRARY CACHE LOCK
.
开头说了半天废话,后面给出了两个解决方案,进行事件跟踪或者查询X$KGLLK表,还是后者容易一点,
搜寻半天,终于问到sysdna登录的方法(权限低,没密码)。第一次用X$表,用sysdba手还有点发抖(那可是正式的业务系统),敲入命令发现运行了很久才反应,找出一个SID了,仔细一看,居然是一个后台维护终端,程序是TOAD,状态INACTIVE。询问相关人员,从未ALTER该表,看来是TOAD看表结构或者数据的时候出了问题,kill掉终于正常,索引很快建完。只是INACTIVE的会话居然也持有锁,真是奇怪。工具太好了,也是双刃剑啊。
篇5:Visual C++ ADO数据库编程入门(上)数据库教程
ado|c++|visual|编程|数据|数据库
ADO 是目前在Windows环境中比较流行的客户端数据库编程技术,Visual C++ ADO数据库编程入门(上)数据库教程
。ADO是建立在OLE DB底层技术之上的高级编程接口,因而它兼具有强大的数据处理功能(处理各种不同类型的数据源、分布式的数据处理等等)和极其简单、易用的编程接口,因而得到了广泛的应用。而且按微软公司的意图,OLE DB和ADO将逐步取代 ODBC和DAO。现在介绍ADO各种应用的文章和书籍有很多,本文着重站在初学者的角度,简要探讨一下在VC++中使用ADO编程时的一些问题。我们希望阅读本文之前,您对ADO技术的基本原理有一些了解。一、在VC++中使用ADO编程
ADO实际上就是由一组Automation对象构成的组件,因此可以象使用其它任何Automation对象一样使用ADO。ADO中最重要的对象有三个:Connection、Command和Recordset,它们分别表示连接对象、命令对象和记录集对象。如果您熟悉使用MFC中的ODBC类(CDatabase、CRecordset)编程,那么学习ADO编程就十分容易了。
使用ADO编程时可以采用以下三种方法之一:
1、使用预处理指令#import
#import “C:\Program Files\Common Files ystem\ADO\msado15.dll” \
no_namespace rename(“EOF”, “EndOfFile”)
但要注意不能放在stdAfx.h文件的开头,而应该放在所有include指令的后面。否则在编译时会出错。
程序在编译过程中,VC++会读出msado15.dll中的类型库信息,自动产生两个该类型库的头文件和实现文件msado15.tlh和msado15.tli(在您的Debug或Release目录下)。在这两个文件里定义了ADO的所有对象和方法,以及一些枚举型的常量等。我们的程序只要直接调用这些方法就行了,与使用MFC中的COleDispatchDriver类调用Automation对象十分类似。
2、使用MFC中的CIDispatchDriver
就是通过读取msado15.dll中的类型库信息,建立一个COleDispatchDriver类的派生类,然后通过它调用ADO对象。
3、直接用COM提供的API
如使用如下代码:
CLSID clsid;
HRESULT hr = ::CLSIDFromProgID(L“ADODB.Connection”, &clsid);
if(FAILED(hr))
{...}
::CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)
&pDispatch);
if(FAILED(hr))
{...}
以上三种方法,第一和第二种类似,可能第一种好用一些,第三种编程可能最麻烦。但可能第三种方法也是效率最高的,程序的尺寸也最小,并且对ADO的控制能力也最强。
据微软资料介绍,第一种方法不支持方法调用中的默认参数,当然第二种方法也是这样,但第三种就不是这样了。采用第三种方法的水平也最高。当你需要绕过ADO而直接调用OLE DB底层的方法时,就一定要使用第三种方法了。
ADO编程的关键,就是熟练地运用ADO提供的各种对象(object)、方法(method)、属性(property)和容器(collection)。另外,如果是在MS SQL或Oracle等大型数据库上编程,还要能熟练使用SQL语言。
二、使用#import方法的编程步骤
这里建议您使用#import的方法,因为它易学、易用,代码也比较简洁。
1、添加#import指令
打开stdafx.h文件,将下列内容添加到所有的include指令之后:
#include
#import “C:\Program Files\Common Files ystem\ADO\msado15.dll” \
no_namespace rename(“EOF”, “adoEOF”)
其中icrsint.h文件包含了VC++扩展的一些预处理指令、宏等的定义,用于COM编程时使用。
2、定义_ConnectionPtr型变量,并建立数据库连接
建立了与数据库服务器的连接后,才能进行其他有关数据库的访问和操作。ADO使用Connection对象来建立与数据库服务器的连接,所以它相当于MFC中的CDatabase类。和CDatabase类一样,调用Connection对象的Open方法即可建立与服务器的连接。
数据类型 _ConnectionPtr实际上就是由类模板_com_ptr_t而得到的一个具体的实例类,其定义可以到msado15.tlh、comdef.h 和comip.h这三个文件中找到。在msado15.tlh中有:
_COM_SMARTPTR_TYPEDEF(_Collection, __uuidof(_Collection));
经宏扩展后就得到了_ConnectionPtr类。_ConnectionPtr类封装了Connection对象的Idispatch接口指针,及一些必要的操作。我们就是通过这个指针来操纵Connection对象。类似地,后面用到的_CommandPtr和_RecordsetPtr类型也是这样得到的,它们分别表示命令对象指针和记录集对象的指针。
(1)、连接到MS SQL Server
注意连接字符串的格式,提供正确的连接字符串是成功连接到数据库服务器的第一步,有关连接字符串的详细信息参见微软MSDN Library光盘。
本例连接字符串中的server_name,database_name,user_name和password在编程时都应该替换成实际的内容。
_ConnectionPtr pMyConnect=NULL;
HRESULT hr=pMyConnect.CreateInstance(__uuidof(Connection)));
if(FAILED(hr))return;
_bstr_t strConnect=“Provider=SQLOLEDB; Server=server_name;”
“Database=database_name; uid=user_name; pwd=password;”;
//connecting to the database server now:
try{pMyConnect->Open(strConnect,“”,“”,NULL);}
catch (_com_error &e)
{
::MessageBox(NULL,e.Description,“警告”,MB_OK │ MB_ICONWARNING);
}
注意Connection对象的Open方法中的连接字符串参数必须是BSTR或_bstr_t类型。另外,本例是直接通过OLE DB Provider建立连接,所以无需建立数据源。
(2)、通过ODBC Driver连接到Database Server连接字符串格式与直接用ODBC编程时的差不多:
_bstr_t strConnect=“DSN=datasource_name; Database=database_name; uid=user_name; pwd=password;”;
此时与ODBC编程一样,必须先建立数据源。
3、定义_RecordsetPtr型变量,并打开数据集
定义_RecordsetPtr型变量,然后通过它调用Recordset对象的Open方法,即可打开一个数据集。所以Recordset对象与MFC中的CRecordset类类似,它也有当前记录、当前记录指针的概念。如:
_RecordsetPtr m_pRecordset;
if(!FAILED(m_pRecordset.CreateInstance( __uuidof( Recordset )))
{
m_pDoc->m_initialized=FALSE;
return;
}
try{
m_pRecordset->Open(_variant_t(“mytable”),
_variant_t((IDispatch *)pMyConnect,true), adOpenKeyset,
adLockOptimistic, adCmdTable);
}
catch (_com_error &e)
{
::MessageBox(NULL,“无法打开mytable表。”,“提示”,
MB_OK │ MB_ICONWARNING);
}
Recordset对象的Open方法非常重要,它的第一个参数可以是一个SQL语句、一个表的名字或一个命令对象等等;第二个参数就是前面建立的连接对象的指针。此外,用Connection和Command对象的Execute方法也能得到记录集,但是只读的。
4、读取当前记录的数据
我认为读取数据的最方便的方法如下:
try{
m_pRecordset->MoveFirst();
while(m_pRecordset->adoEOF==VARIANT_FALSE)
{
//Retrieve column's value:
CString sName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
(_variant_t(“name”))->Value);
short cAge=(short)(m_pRecordset->Fields->GetItem
(_variant_t(“age”))->Value);
//Do something what you want to do:
......
m_pRecordset->MoveNext();
}
}//try
catch (_com_error &e)
{
CString str=(char*)e.Description();
::MessageBox(NULL,str+“\n又出毛病了。”,“提示”,
MB_OK │ MB_ICONWARNING);
}
本例中的name和age都是字段名,读取的字段值分别保存在sName和cAge变量内。例中的Fields是Recordset对象的容器,GetItem方法返回的是Field对象,而Value则是Field对象的一个属性(即该字段的值)。通过此例,应掌握操纵对象属性的方法。例如,要获得Field 对象的Value属性的值可以直接用属性名Value来引用它(如上例),但也可以调用Get方法,例如:
CString sName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
(_variant_t(“name”))->GetValue());
从此例还可以看到,判断是否到达记录集的末尾,使用记录集的adoEOF属性,其值若为真即到了结尾,反之则未到。判断是否到达记录集开头,则可用BOF属性。
另外,读取数据还有一个方法,就是定义一个绑定的类,然后通过绑定的变量得到字段值(详见后面的介绍)。
5、修改数据
方法一:
try{
m_pRecordset->MoveFirst();
while(m_pRecordset->adoEOF==VARIANT_FALSE)
{
m_pRecordset->Fields->GetItem
(_variant_t(“姓名”))->Value=_bstr_t(“赵薇”);
......
m_pRecordset->Update();
m_pRecordset->MoveNext();
}
}//try
改变了Value属性的值,即改变了字段的值。
方法二:
m_pRecordset->Fields->GetItem
(_variant_t(“姓名”))->PutValue(_bstr_t(“赵薇”));
方法三:就是用定义绑定类的方法(详见后面的介绍)。
6、添加记录
新记录添加成功后,即自动成为当前记录。AddNew方法有两种形式,一个含有参数,而另一个则不带参数。
方法一(不带参数):
// Add new record into this table:
try{
if(!m_pRecordset->Supports(adAddNew)) return;
m_pRecordset->AddNew();
m_pRecordset->Fields->GetItem
(_variant_t(“姓名”))->Value=_bstr_t(“赵薇”);
m_pRecordset->Fields->GetItem
(_variant_t(“性别”))->Value=_bstr_t(“女”);
m_pRecordset->Fields->GetItem
(_variant_t(“age”))->Value=_variant_t((short)20);
m_pRecordset->Fields->GetItem
(_variant_t(“marry”))->Value=_bstr_t(“未婚”);
m_pRecordset->Update();
}//try
catch (_com_error &e)
{
::MessageBox(NULL, “又出毛病了。”,“提示”,MB_OK │ MB_ICONWARNING);
}
这种方法弄完了还要调用Update()。
方法二(带参数):
_variant_t varName[4],narValue[4];
varName[0] = L“姓名”;
varName[1] = L“性别”;
varName[2] = L“age”;
varName[3] = L“marry”;
narValue[0]=_bstr_t(“赵薇”);
narValue[1]=_bstr_t(“女”);
narValue[2]=_variant_t((short)20);
narValue[3]=_bstr_t(“未婚”);
const int nCrit = sizeof varName / sizeof varName[0];
// Create SafeArray Bounds and initialize the array
SAFEARRAYBOUND rgsaName[1],rgsaValue[1];
rgsaName[0].lLbound = 0;
rgsaName[0].cElements = nCrit;
SAFEARRAY *psaName = SafeArrayCreate( VT_VARIANT, 1, rgsaName );
rgsaValue[0].lLbound = 0;
rgsaValue[0].cElements = nCrit;
SAFEARRAY *psaValue = SafeArrayCreate( VT_VARIANT, 1, rgsaValue );
// Set the values for each element of the array
HRESULT hr1=S_OK.hr2=S_OK;
for( long i = 0 ; i < nCrit && SUCCEEDED( hr1 ) && SUCCEEDED( hr2 );i++)
{
hr1=SafeArrayPutElement(psaName, &i,&varName[i]);
hr2=SafeArrayPutElement(psaValue, &i,&narValue[i]); }
// Initialize and fill the SafeArray
VARIANT vsaName,vsaValue;
vsaName.vt = VT_VARIANT │ VT_ARRAY;
vsaValue.vt = VT_VARIANT │ VT_ARRAY;
V_ARRAY(&vsaName) = psaName;//&vsaName->parray=psaName;
//see definition in oleauto.h file.
V_ARRAY(&vsaValue) = psaValue;
// Add a new record:
m_pRecordset->AddNew(vsaName,vsaValue);
这种方法不需要调用Update,因为添加后,ADO会自动调用它,
此方法主要是使用SafeArray挺麻烦。
方法三:就是用定义绑定类的方法(详见后面的介绍)。
7、删除记录
调用Recordset的Delete方法就行了,删除的是当前记录。要了解Delete的其它用法请查阅参考文献。
try{
m_pRecordset->MoveFirst();
while(m_pRecordset->adoEOF==VARIANT_FALSE)
{
CString sName=(char*)(_bstr_t)(m_pRecordset->Fields->GetItem
(_variant_t(“姓名”))->Value);
if(::MessageBox(NULL,“姓名=”+sName+“\n删除她吗?”,
“提示”,MB_YESNO │ MB_ICONWARNING)==IDYES)
{
m_pRecordset->Delete(adAffectCurrent);
m_pRecordset->Update();
}
m_pRecordset->MoveNext();
}
}//try
catch (_com_error &e)
{
::MessageBox(NULL,“又出毛病了。”,“提示”,MB_OK │ MB_ICONWARNING);
}
8、使用带参数的命令
Command对象所代表的就是一个Provider能够理解的命令,如SQL语句等。使用Command对象的关键就是把表示命令的语句设置到CommandText属性中,然后调用Command对象的Execute方法就行了。一般情况下在命令中无需使用参数,但有时使用参数,可以增加其灵活性和效率。
(1). 建立连接、命令对象和记录集对象
本例中表示命令的语句就是一个SQL语句(SELECT语句)。SELECT语句中的问号?就代表参数,如果要多个参数,就多放几个问号,每个问号代表一个参数。
_ConnectionPtr Conn1;
_CommandPtr Cmd1;
ParametersPtr *Params1 = NULL; // Not an instance of a smart pointer.
_ParameterPtr Param1;
_RecordsetPtr Rs1;
try
{
// Create Connection Object (1.5 Version)
Conn1.CreateInstance( __uuidof( Connection ) );
Conn1->ConnectionString = bstrConnect;
Conn1->Open( bstrEmpty, bstrEmpty, bstrEmpty, -1 );
// Create Command Object
Cmd1.CreateInstance( __uuidof( Command ) );
Cmd1->ActiveConnection = Conn1;
Cmd1->CommandText = _bstr_t(“SELECT * FROM mytable WHERE age< ?”);
}//try
要注意命令对象必须与连接对象关联起来才能起作用,本例中将命令对象的ActiveConnection属性设置为连接对象的指针,即为此目的:
Cmd1->ActiveConnection = Conn1;
(2). 创建参数对象,并给参数赋值
// Create Parameter Object
Param1 = Cmd1->CreateParameter( _bstr_t(bstrEmpty),
adInteger,
adParamInput,
-1,
_variant_t( (long) 5) );
Param1->Value = _variant_t( (long) 5 );
Cmd1->Parameters->Append( Param1 );
用命令对象的方法来创建一个参数对象,其中的长度参数(第三个)如果是固定长度的类型,就填-1,如果是字符串等可变长度的就填其实际长度。Parameters是命令对象的一个容器,它的Append方法就是把创建的参数对象追加到该容器里。Append进去的参数按先后顺序与SQL语句中的问号从左至右一一对应。
(3). 执行命令打开记录集
// Open Recordset Object
Rs1 = Cmd1->Execute( &vtEmpty, &vtEmpty2, adCmdText );
但要注意,用Command和Connection对象的Execute方法得到的Recordset是只读的。因为在打开Recordset之前,我们无法设置它的LockType属性(其默认值为只读)。而在打开之后设置LockType不起作用。
我发现用上述方法得到记录集Rs1后,不但Rs1中的记录无法修改,即使直接用SQL语句修改同一表中任何记录都不行。
要想能修改数据,还是要用Recordset自己的Open方法才行,如:
try{
m_pRecordset->Open((IDispatch *) Cmd1, vtMissing,
adOpenStatic, adLockOptimistic, adCmdUnspecified);
}
catch (_com_error &e)
{
::MessageBox(NULL,“mytable表不存在。”,“提示”,MB_OK │ MB_ICONWARNING);
}
Recordset对象的Open方法真是太好了,其第一个参数可以是SQL语句、表名字、命令对象指针等等。
9、响应ADO的通知事件
通知事件就是当某个特定事件发生时,由Provider通知客户程序,换句话说,就是由Provider调用客户程序中的一个特定的方法(即事件的处理函数)。所以为了响应一个事件,最关键的就是要实现事件的处理函数。
(1). 从ConnectionEventsVt接口派生出一个类
为了响应_Connection的通知事件,应该从ConnectionEventsVt接口派生出一个类:
class CConnEvent : public ConnectionEventsVt
{
private:
ULONG m_cRef;
public:
CConnEvent() { m_cRef = 0; };
~CConnEvent() {};
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
STDMETHODIMP raw_InfoMessage(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection);
STDMETHODIMP raw_BeginTransComplete(
LONG TransactionLevel,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection);
......
};
(2). 实现每一个事件的处理函数(凡是带raw_前缀的方法都把它实现了):
STDMETHODIMP CConnEvent::raw_InfoMessage(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};
有些方法虽然你并不需要,但也必须实现它,只需简单地返回一个S_OK即可。但如果要避免经常被调用,还应在其中将adStatus参数设置为adStatusUnwantedEvent,则在本次调用后,以后就不会被调用了。
另外还必须实现QueryInterface, AddRef, 和Release三个方法:
STDMETHODIMP CConnEvent::QueryInterface(REFIID riid, void ** ppv)
{
*ppv = NULL;
if (riid == __uuidof(IUnknown) ││
riid == __uuidof(ConnectionEventsVt)) *ppv = this;
if (*ppv == NULL)
return ResultFromScode(E_NOINTERFACE);
AddRef();
return NOERROR;
}
STDMETHODIMP_(ULONG) CConnEvent::AddRef() { return ++m_cRef; };
STDMETHODIMP_(ULONG) CConnEvent::Release()
{
if (0 != --m_cRef) return m_cRef;
delete this;
return 0;
}
(3). 开始响应通知事件
// Start using the Connection events
IConnectionPointContainer *pCPC = NULL;
IConnectionPoint *pCP = NULL;
hr = pConn.CreateInstance(__uuidof(Connection));
if (FAILED(hr)) return;
hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
(void **)&pCPC);
if (FAILED(hr)) return;
hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
pCPC->Release();
if (FAILED(hr)) return;
pConnEvent = new CConnEvent();
hr = pConnEvent->QueryInterface(__uuidof(IUnknown), (void **) &pUnk);
if (FAILED(hr)) return rc;
hr = pCP->Advise(pUnk, &dwConnEvt);
pCP->Release();
if (FAILED(hr)) return;
pConn->Open(“dsn=Pubs;”, “sa”, “”, adConnectUnspecified);
也就是说在连接(Open)之前就做这些事。
(4). 停止响应通知事件
pConn->Close();
// Stop using the Connection events
hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
(void **) &pCPC);
if (FAILED(hr)) return;
hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
pCPC->Release();
if (FAILED(hr)) return rc;
hr = pCP->Unadvise( dwConnEvt );
pCP->Release();
if (FAILED(hr)) return;
在连接关闭之后做这件事。
篇6:Visual C++ ADO数据库编程入门(下)数据库教程
ado|c++|visual|编程|数据|数据库
10、邦定数据定义一个绑定类,将其成员变量绑定到一个指定的记录集,以方便于访问记录集的字段值,
(1). 从CADORecordBinding派生出一个类:
class CCustomRs : public CADORecordBinding
{
BEGIN_ADO_BINDING(CCustomRs)
ADO_VARIABLE_LENGTH_ENTRY2(3, adVarChar, m_szau_fname,
sizeof(m_szau_fname), lau_fnameStatus, false)
ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_szau_lname,
sizeof(m_szau_lname), lau_lnameStatus, false)
ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_szphone,
sizeof(m_szphone), lphoneStatus, true)
END_ADO_BINDING()
public:
CHAR m_szau_fname[22];
ULONG lau_fnameStatus;
CHAR m_szau_lname[42];
ULONG lau_lnameStatus;
CHAR m_szphone[14];
ULONG lphoneStatus;
};
其中将要绑定的字段与变量名用BEGIN_ADO_BINDING宏关联起来。每个字段对应于两个变量,一个存放字段的值,另一个存放字段的状态。字段用从1开始的序号表示,如1,2,3等等。
特别要注意的是:如果要绑定的字段是字符串类型,则对应的字符数组的元素个数一定要比字段长度大2(比如m_szau_fname[22],其绑定的字段au_fname的长度实际是20),不这样绑定就会失败。我分析多出的2可能是为了存放字符串结尾的空字符null和BSTR字符串开头的一个字(表示BSTR的长度)。这个问题对于初学者来说可能是一个意想不到的问题。
CADORecordBinding类的定义在icrsint.h文件里,内容是:
class CADORecordBinding
{
public:
STDMETHOD_(const ADO_BINDING_ENTRY*, GetADOBindingEntries) (VOID) PURE;
};
BEGIN_ADO_BINDING宏的定义也在icrsint.h文件里,内容是:
#define BEGIN_ADO_BINDING(cls) public: \
typedef cls ADORowClass; \
const ADO_BINDING_ENTRY* STDMETHODCALLTYPE GetADOBindingEntries() { \
static const ADO_BINDING_ENTRY rgADOBindingEntries[] = {
ADO_VARIABLE_LENGTH_ENTRY2宏的定义也在icrsint.h文件里:
#define ADO_VARIABLE_LENGTH_ENTRY2(Ordinal, DataType, Buffer, Size, Status, Modify)\
{Ordinal, \
DataType, \
0, \
0, \
Size, \
offsetof(ADORowClass, Buffer), \
offsetof(ADORowClass, Status), \
0, \
classoffset(CADORecordBinding, ADORowClass), \
Modify},
#define END_ADO_BINDING宏的定义也在icrsint.h文件里:
#define END_ADO_BINDING() {0, adEmpty, 0, 0, 0, 0, 0, 0, 0, FALSE}};\
return rgADOBindingEntries;}
(2). 绑定
_RecordsetPtr Rs1;
IADORecordBinding *picRs=NULL;
CCustomRs rs;
......
Rs1->QueryInterface(__uuidof(IADORecordBinding),
(LPVOID*)&picRs));
picRs->BindToRecordset(&rs);
派生出的类必须通过IADORecordBinding接口才能绑定,调用它的BindToRecordset方法就行了。
(3). rs中的变量即是当前记录字段的值
//Set sort and filter condition:
// Step 4: Manipulate the data
Rs1->Fields->GetItem(“au_lname”)->Properties->GetItem(“Optimize”)->Value = true;
Rs1->Sort = “au_lname ASC”;
Rs1->Filter = “phone LIKE '415 5*'”;
Rs1->MoveFirst();
while (VARIANT_FALSE == Rs1->EndOfFile)
{
printf(“Name: %s\t %s\tPhone: %s\n”,
(rs.lau_fnameStatus == adFldOK ? rs.m_szau_fname : “”),
(rs.lau_lnameStatus == adFldOK ? rs.m_szau_lname : “”),
(rs.lphoneStatus == adFldOK ? rs.m_szphone : “”));
if (rs.lphoneStatus == adFldOK)
strcpy(rs.m_szphone, “777”);
TESTHR(picRs->Update(&rs)); // Add change to the batch
Rs1->MoveNext();
}
Rs1->Filter = (long) adFilterNone;
......
if (picRs) picRs->Release();
Rs1->Close();
pConn->Close();
只要字段的状态是adFldOK,就可以访问。如果修改了字段,不要忘了先调用picRs的Update(注意不是Recordset的Update),然后才关闭,也不要忘了释放picRs(即picRs->Release();)。
(4). 此时还可以用IADORecordBinding接口添加新纪录
if(FAILED(picRs->AddNew(&rs)))
......
11. 访问长数据
在Microsoft SQL中的长数据包括text、image等这样长类型的数据,作为二进制字节来对待。
可以用Field对象的GetChunk和AppendChunk方法来访问。每次可以读出或写入全部数据的一部分,它会记住上次访问的位置。但是如果中间访问了别的字段后,就又得从头来了。
请看下面的例子:
//写入一张照片到数据库:
VARIANT varChunk;
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[1];
//VT_ARRAY │ VT_UI1
CFile f(“h:\\aaa.jpg”,Cfile::modeRead);
BYTE bVal[ChunkSize+1];
UINT uIsRead=0;
//Create a safe array to store the array of BYTES
while(1)
{
uIsRead=f.Read(bVal,ChunkSize);
if(uIsRead==0)break;
rgsabound[0].cElements =uIsRead;
rgsabound[0].lLbound = 0;
psa = SafeArrayCreate(VT_UI1,1,rgsabound);
for(long index=0;index
{
if(FAILED(SafeArrayPutElement(psa,&index,&bVal[index])))
::MessageBox(NULL,“啊,又出毛病了。”,“提示”,MB_OK │ MB_ICONWARNING);
}
varChunk.vt = VT_ARRAY│VT_UI1;
varChunk.parray = psa;
try{
m_pRecordset->Fields->GetItem(“photo”)->AppendChunk(varChunk);
}
catch (_com_error &e)
{
CString str=(char*)e.Description();
::MessageBox(NULL,str+“\n又出毛病了。”,“提示”,MB_OK │ MB_ICONWARNING);
}
::VariantClear(&varChunk);
::SafeArrayDestroyData( psa);
if(uIsRead
}//while(1)
f.Close();
//从数据库读一张照片:
CFile f;
f.Open(“h:\\bbb.jpg”,Cfile::modeWrite│Cfile::modeCreate);
long lPhotoSize = m_pRecordset->Fields->Item[“photo”]->ActualSize;
long lIsRead=0;
_variant_t varChunk;
BYTE buf[ChunkSize];
while(lPhotoSize>0)
{
lIsRead=lPhotoSize>=ChunkSize? ChunkSize:lPhotoSize;
varChunk = m_pRecordset->Fields->
Item[“photo”]->GetChunk(lIsRead);
for(long index=0;index
{
::SafeArrayGetElement(varChunk.parray,&index,buf+index);
}
f.Write(buf,lIsRead);
lPhotoSize-=lIsRead;
}//while()
f.Close();
12. 使用SafeArray问题
学会使用SafeArray也是很重要的,因为在ADO编程中经常要用。它的主要目的是用于automation中的数组型参数的传递。因为在网络环境中,数组是不能直接传递的,而必须将其包装成SafeArray。实质上SafeArray就是将通常的数组增加一个描述符,说明其维数、长度、边界、元素类型等信息。SafeArray也并不单独使用,而是将其再包装到VARIANT类型的变量中,然后才作为参数传送出去。在VARIANT的vt成员的值如果包含VT_ARRAY│...,那么它所封装的就是一个SafeArray,它的parray成员即是指向SafeArray的指针。SafeArray中元素的类型可以是VARIANT能封装的任何类型,包括VARIANT类型本身。
使用SafeArray的具体步骤:
方法一:
包装一个SafeArray:
(1). 定义变量,如:
VARIANT varChunk;
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[1];
(2). 创建SafeArray描述符:
uIsRead=f.Read(bVal,ChunkSize);//read array from a file.
if(uIsRead==0)break;
rgsabound[0].cElements =uIsRead;
rgsabound[0].lLbound = 0;
psa = SafeArrayCreate(VT_UI1,1,rgsabound);
(3). 放置数据元素到SafeArray:
for(long index=0;index
{
if(FAILED(SafeArrayPutElement(psa,&index,&bVal[index])))
::MessageBox(NULL,“出毛病了。”,“提示”,MB_OK │ MB_ICONWARNING);
}
一个一个地放,挺麻烦的。
(4). 封装到VARIANT内:
varChunk.vt = VT_ARRAY│VT_UI1;
varChunk.parray = psa;
这样就可以将varChunk作为参数传送出去了。
读取SafeArray中的数据的步骤:
(1). 用SafeArrayGetElement一个一个地读
BYTE buf[lIsRead];
for(long index=0;index
{
::SafeArrayGetElement(varChunk.parray,&index,buf+index);
}
就读到缓冲区buf里了,
方法二:
使用SafeArrayAccessData直接读写SafeArray的缓冲区:
(1). 读缓冲区:
BYTE *buf;
SafeArrayAccessData(varChunk.parray, (void **)&buf);
f.Write(buf,lIsRead);
SafeArrayUnaccessData(varChunk.parray);
(2). 写缓冲区:
BYTE *buf;
::SafeArrayAccessData(psa, (void **)&buf);
for(long index=0;index
{
buf[index]=bVal[index];
}
::SafeArrayUnaccessData(psa);
varChunk.vt = VT_ARRAY│VT_UI1;
varChunk.parray = psa;
这种方法读写SafeArray都可以,它直接操纵SafeArray的数据缓冲区,比用SafeArrayGetElement和SafeArrayPutElement速度快。特别适合于读取数据。但用完之后不要忘了调用::SafeArrayUnaccessData(psa),否则会出错的。
13. 使用书签( bookmark )
书签可以唯一标识记录集中的一个记录,用于快速地将当前记录移回到已访问过的记录,以及进行过滤等等。Provider会自动为记录集中的每一条记录产生一个书签,我们只需要使用它就行了。我们不能试图显示、修改或比较书签。ADO用记录集的Bookmark属性表示当前记录的书签。
用法步骤:
(1). 建立一个VARIANT类型的变量
_variant_t VarBookmark;
(2). 将当前记录的书签值存入该变量
也就是记录集的Bookmark属性的当前值。
VarBookmark = rst->Bookmark;
(3). 返回到先前的记录
将保存的书签值设置到记录集的书签属性中:
// Check for whether bookmark set for a record
if (VarBookmark.vt == VT_EMPTY)
printf(“No Bookmark set!\n”);
else
rst->Bookmark = VarBookmark;
设置完后,当前记录即会移动到该书签指向的记录。
14、设置过滤条件
Recordset对象的Filter属性表示了当前的过滤条件。它的值可以是以AND或OR连接起来的条件表达式(不含WHERE关键字)、由书签组成的数组或ADO提供的FilterGroupEnum枚举值。为Filter属性设置新值后Recordset的当前记录指针会自动移动到满足过滤条件的第一个记录。例如:
rst->Filter = _bstr_t (“姓名='赵薇' AND 性别=’女’”);
在使用条件表达式时应注意下列问题:
(1)、可以用圆括号组成复杂的表达式
例如:
rst->Filter = _bstr_t (“(姓名='赵薇' AND 性别=’女’) OR AGE<25”);
但是微软不允许在括号内用OR,然后在括号外用AND,例如:
rst->Filter = _bstr_t (“(姓名='赵薇' OR 性别=’女’) AND AGE<25”);
必须修改为:
rst->Filter = _bstr_t (“(姓名='赵薇' AND AGE<25) OR (性别=’女’ AND AGE<25)”);
(2)、表达式中的比较运算符可以是LIKE
LIKE后被比较的是一个含有通配符*的字符串,星号表示若干个任意的字符。
字符串的首部和尾部可以同时带星号*
rst->Filter = _bstr_t (“姓名 LIKE '*赵*' ”);
也可以只是尾部带星号:
rst->Filter = _bstr_t (“姓名 LIKE '赵*' ”);
Filter属性值的类型是Variant,如果过滤条件是由书签组成的数组,则需将该数组转换为SafeArray,然后再封装到一个VARIANT或_variant_t型的变量中,再赋给Filter属性。
15、索引与排序
(1)、建立索引
当以某个字段为关键字用Find方法查找时,为了加快速度可以以该字段为关键字在记录集内部临时建立索引。只要将该字段的Optimize属性设置为true即可,例如:
pRst->Fields->GetItem(“姓名”)->Properties->
GetItem(“Optimize”)->PutValue(“True”);
pRst->Find(“姓名 = '赵薇'”,1,adSearchForward);
......
pRst->Fields->GetItem(“姓名”)->Properties->
GetItem(“Optimize”)->PutValue(“False”);
pRst->Close();
说明:Optimize属性是由Provider提供的属性(在ADO中称为动态属性),ADO本身没有此属性。
(2)、排序
要排序也很简单,只要把要排序的关键字列表设置到Recordset对象的Sort属性里即可,例如:
pRstAuthors->CursorLocation = adUseClient;
pRstAuthors->Open(“SELECT * FROM mytable”,
_variant_t((IDispatch *) pConnection),
adOpenStatic, adLockReadOnly, adCmdText);
......
pRst->Sort = “姓名 DESC, 年龄 ASC”;
关键字(即字段名)之间用逗号隔开,如果要以某关键字降序排序,则应在该关键字后加一空格,再加DESC(如上例)。升序时ASC加不加无所谓。本操作是利用索引进行的,并未进行物理排序,所以效率较高。
但要注意,在打开记录集之前必须将记录集的CursorLocation属性设置为adUseClient,如上例所示。Sort属性值在需要时随时可以修改。
16、事务处理
ADO中的事务处理也很简单,只需分别在适当的位置调用Connection对象的三个方法即可,这三个方法是:
(1)、在事务开始时调用
pCnn->BeginTrans();
(2)、在事务结束并成功时调用
pCnn->CommitTrans ();
(3)、在事务结束并失败时调用
pCnn->RollbackTrans ();
在使用事务处理时,应尽量减小事务的范围,即减小从事务开始到结束(提交或回滚)之间的时间间隔,以便提高系统效率。需要时也可在调用BeginTrans()方法之前,先设置Connection对象的IsolationLevel属性值,详细内容参见MSDN中有关ADO的技术资料。
三、使用ADO编程常见问题解答
以下均是针对MS SQL 7.0编程时所遇问题进行讨论。
1、连接失败可能原因
Enterprise Managemer内,打开将服务器的属性对话框,在Security选项卡中,有一个选项Authentication。
如果该选项是Windows NT only,则你的程序所用的连接字符串就一定要包含Trusted_Connection参数,并且其值必须为yes,如:
“Provider=SQLOLEDB;Server=888;Trusted_Connection=yes”
“;Database=master;uid=lad;”;
如果不按上述操作,程序运行时连接必然失败。
如果Authentication选项是SQL Server and Windows NT,则你的程序所用的连接字符串可以不包含Trusted_Connection参数,如:
“Provider=SQLOLEDB;Server=888;Database=master;uid=lad;pwd=111;”;
因为ADO给该参数取的默认值就是no,所以可以省略。我认为还是取默认值比较安全一些。
2、改变当前数据库的方法
使用Tansct-SQL中的USE语句即可。
3、如何判断一个数据库是否存在
(1)、可打开master数据库中一个叫做SCHEMATA的视图,其内容列出了该服务器上所有的数据库名称。
(2) 、更简便的方法是使用USE语句,成功了就存在;不成功,就不存在。例如:
try{
m_pConnect->Execute ( _bstr_t(“USE INSURANCE_”),NULL,
adCmdText│adExecuteNoRecords );
}
catch (_com_error &e)
{
blSuccess=FALSE;
CString str=“数据库INSURANCE_2002不存在!\n”;
str+=e.Description();
::MessageBox(NULL,str,“警告”,MB_OK │ MB_ICONWARNING);
}
4、判断一个表是否存在
(1)、同样判断一个表是否存在,也可以用是否成功地打开它来判断,十分方便,例如:
try{
m_pRecordset->Open(_variant_t(“mytable”),
_variant_t((IDispatch *)m_pConnection,true), adOpenKeyset,
adLockOptimistic, adCmdTable);
}
catch (_com_error &e)
{
::MessageBox(NULL,“该表不存在。”,“提示”,MB_OK │ MB_ICONWARNING);
}
(2)、要不然可以采用麻烦一点的办法,就是在MS-SQL服务器上的每个数据库中都有一个名为sysobjects的表,查看此表的内容即知指定的表是否在该数据库中。
(3)、同样,每个数据库中都有一个名为TABLES的视图(View),查看此视图的内容即知指定的表是否在该数据库中。
5、类型转换问题
(1)、类型VARIANT_BOOL
类型VARIANT_BOOL等价于short类型。The VARIANT_BOOL is equivalent to short. see it's definition below:
typdef short VARIANT_BOOL
(2)、_com_ptr_t类的类型转换
_ConnectionPtr可以自动转换成IDspatch*类型,这是因为_ConnectionPtr实际上是_com_ptr_t类的一个实例,而这个类有此类型转换函数。
同理,_RecordsetPtr和_CommandPtr也都可以这样转换。
(3)、_bstr_t和_variant_t类
在ADO编程时,_bstr_t和_variant_t这两个类很有用,省去了许多BSTR和VARIANT类型转换的麻烦。
6、打开记录集时的问题
在打开记录集时,在调用Recordset的Open方法时,其最后一个参数里一定不能包含adAsyncExecute,否则将因为是异步操作,在读取数据时无法读到数据。
7、异常处理问题
对所有调用ADO的语句一定要用try和catch语句捕捉异常,否则在发生异常时,程序会异常退出。
8、使用SafeArray问题
在初学使用中,我曾遇到一个伤脑筋的问题,一定要注意:
在定义了SAFEARRAY的指针后,如果打算重复使用多次,则在中间可以调用::SafeArrayDestroyData释放数据,但决不能调用::SafeArrayDestroyDescriptor,否则必然出错,即使调用SafeArrayCreate也不行。例如:
SAFEARRAY *psa;
......
//When the data are no longer to be used:
::SafeArrayDestroyData( psa);
我分析在定义psa指针时,一个SAFEARRAY的实例(也就是SAFEARRAY描述符)也同时被自动建立了。但是只要一调用::SafeArrayDestroyDescriptor,描述符就被销毁了。
所以我认为::SafeArrayDestroyDescriptor可以根本就不调用,即使调用也必须在最后调用。
9、重复使用命令对象问题
一个命令对象如果要重复使用多次(尤其是带参数的命令),则在第一次执行之前,应将它的Prepared属性设置为TRUE。这样会使第一次执行减慢,但却可以使以后的执行全部加快。
10、绑定字符串型字段问题
如果要绑定的字段是字符串类型,则对应的字符数组的元素个数一定要比字段长度大2(比如m_szau_fname[22],其绑定的字段au_fname的长度实际是20),不这样绑定就会失败。
11、使用AppendChunk的问题
当用AddNew方法刚刚向记录集内添加一个新记录之后,不能首先向一个长数据字段(image类型)写入数据,必须先向其他字段写入过数据之后,才能调用AppendChunk写该字段,否则出错。也就是说,AppendChunk不能紧接在AddNew之后。另外,写入其他字段后还必须紧接着调用AppendChunk,而不能调用记录集的Update方法后,才调用AppendChunk,否则调用AppendChunk时也会出错。换句话说,就是必须AppendChunk在前,Update在后。因而这个时候就不能使用带参数的AddNew了,因为带参数的AddNew会自动调用记录集的Update,所以AppendChunk就跑到Update的后面了,就只有出错了!因此,这时应该用不带参数的AddNew。
我推测这可能是MS SQL 7.0的问题,在MS SQL 中则不存在这些问题,但是AppendChunk仍然不能在Update之后。
四、小结
一般情况下,Connection和Command的Execute用于执行不产生记录集的命令,而Recordset的Open用于产生一个记录集,当然也不是绝对的。特别Command主要是用于执行参数化的命令,可以直接由Command对象执行,也可以将Command对象传递给Recordset的Open。
篇7:ADO.NET最佳实践(中)数据库教程
ado
使用DataReader可以在你的应用程序中做以下事情:
I.不需要缓存数据;
II.处理太大而不能存储的数据;
III.需要以只进、只读和快速方式一次性访问数据的,
G.使用一个自定义的强有力的DataSet类型的好处
通过创建一个继承于DataSet的子对象,你可以在运行期间执行类型检查和声明。当你有了一个确定的计划或者为你的DataSet有相关的结构,你就可以创建一个用行和列表述一个对象的DataSet。比如,你表露一个消费者对象的名字属性来取代表露消费者表的一行中的名字列。有关此节详细信息,请参考微软站点上的文章:Working with a Typed DataSet
H.在自定义的DataSet中处理无效数据
通过XSD语言检查你的DataSet确保你的DataSet适当地处理无效引用。nullValue注释使你把BBNull替换成别的字符,String.Empty;或者保留无效引用,抛出错误提示,提示将取决于你应用程序的上下文,默认情况是引用了无效字符。
I.在DataSet中刷新数据
如果你要从数据库刷新你的DataSet,使用DataAdapter.Fill,如果你的DataTable拥有主键,DataAdapter.Fill将根据主键匹配新的行,同时从数据库取值运用到已存在的行。除非已刷新行在再次刷新前被修改,否者它的RowState将会被设置为UnChanged。注意的是如果DataTable没有设置主键,你的DataSet有可能出现重复的值。如果你想从数据库刷新一个表并保留任何表中行的更改,那么你就要首先填充一个新表,然后利用preserveChanges等于true来合并那个DataTable到你的DataSet中去。
J.在DataSet中搜索数据
当你在一个DataSet中查询特殊标准的行时,利用索引查询将会增加你的查询性能。当你给一个DataTable设计主键时,索引同时也创建了。当你为一个DataTable创建DataView时,索引也同时创建了。以下是使用索引查询的一些情况:
I.如果查询与DataTable中标识主键的列顺序相反,使用DataTable.Rows.Find代替DataTable.Select;
II.如果查询包括无主键的列,你可以使用DataView为数据的多重查询改善性能。当你在DataView中使用排序时,查询的同时就会创建一个索引。DataView使用Find和FindRows方法查询DataTable中的数据;
IV.假如你不需要表的排序视图,你也可以利用DataView为DataTable创建一个索引查询。注意的是这仅仅在你执行多重查询时才有优势,如果你只是执行一个简单查询,使用此方法将会降低你的查询效率。
K.DataView的结构
前面也讲过,在给DataTable创建DataView和Sort、RowFilter或者RowStateFilter属性发生更改的同时潜在的也给DataTable创建了索引。创建DataView对象时,如果Sort、RowFilter和RowStateFilter属性也同时指定,那么索引将只创建一次;如果创建一个空的DataView,那么索引至少被创建两次。
L.页面调度
ADO.NET使你可以很清楚地控制从你的数据库返回什么样的数据和有多少数据存储到一个DataSet。以下没有单一的介绍调度一个查询结果,但是当你设计你的应用程序时应该考虑到以下情况:
I.避免在使用DataAdapter.Fill时,在startRecord和maxRecords值上溢出。
II.解决这类问题的办法是使用WHERE语句、ORDER BY语句和TOP断言。
III.还有一种解决办法是使用TOP断言和嵌套的SELECT声明。比如如下代码:
SELECT TOP 10 * FROM
(SELECT TOP 30 * FROM Customers ORDER BY Id ASC) AS Table1 ORDER BY Id DESC
IV.如果你的日期不是经常改变,你可以使用DataSet的存储功能改善执行性能,比如你可以存储相当10页的数据到你的DataSet,然后当用户访问超过在存储区的FirstPage和LastPage时才查询数据库以获得新的数据。
M.有计划地填充DataSet
当使用数据填充DataSet时,DataAdapter.Fill方法使用DataSet已有的计划和SelectCommand返回的数据对DataSet进行填充。如果DataSet中没有与之对应的表将会失败,Fill创建一个表,默认情况下,Fill仅仅定义列和列的类型。你可以通过设置DataAdapter的MissingSchemaAction属性重载默认的Fill方法。举例,要使Fill方法创建表时总是包含主键信息、唯一约束、列属性、是否允许空值、列的最大长度、只读列和自动增量列,指定DataAdapter.MissingSchemaAction为MissingSchemaAction.AddWithKey。作为选择,你也可以在调用DataAdapter.Fill之前调用DataAdpater.FillSchema来保证填充DataSet时计划到位。调用FillSchema会给数据库增加额外的负担来输出Schema信息,所以最好的建议是指定DataSet的计划,或者在调用Fill之前设置DataAdapter的MissingSchemaAction。
N.使用CommandBuilder
CommandBuilder自动地生成基于DataAdapter的SelectCommand的DataAdapter的InsertCommand、UpdateCommand和DeleteCommand属性。提供SelectCommand执行一个简单的SELECT,以下信息介绍使用CommandBuilder的最佳处理。
I.在设计阶段不要使用CommandBuilder,否者产生DataAdapter Command属性的进程将会受到干扰。如果你预先知道你的UPDATE、INSERT和DELETE声明的内容,你应该清楚地指定。一个最好的设计方案是为你的UPDATE、INSERT和DELETE创建存储过程,并在DataAdapter的Command属性中设置和使用它们。
II.CommandBuilder使用SelectCommand决定其他Command属性的值。如果DataAdapter的SelectCommand本身发生变化,应该使用RefreshSchema去刷新Command的属性。
III.只要DataAdapter的Command属性为空,CommandBuilder就仅仅创建一个Command,即使你明确地指定Command的属性值,CommandBuilder也不会重写,所以如果你想创建一个Command并保留以前的属性设置,那么就把Command的属性设置为null。
O.SQL的批声明和处理
很多的数据库都支持在一条命令中使用综合查询或批处理或多条子命令。比如SQL Server中使用“;”。在一条命令中使用综合的多重命令可以有效地减少与数据库之间交互的次数并在你的应用程序中提高效率。比如在你的应用程序中使用批处理完成所有的删除(delete)任务等等。
使用批处理确实提高了效率,但同时也在你的应用程序中管理更新DataSet数据时增加了复杂性。要使得复杂性变简单化,你就要在你的DataSet中为每个DataTable创建一个DataAdapter。
P.使用多个表填充一个DataSet
如果你是用批处理从多个表返回数据并把这些数据填充到一个DataSet,fill方法将会使用第一个表的表名命名第一个表,以后的表命名将会采用在第一个表的表名基础上加上一个递增的数字。举例,下面的代码将逐步说明fill方法的工作原理:
‘Visual Basic
Dim da As SqlDataAdapter = New SqlDataAdapter(“select * from customers;select * from orders;”,myConnection)
Dim ds As DataSet = New DataSet
da.fill(ds,”customers”)
‘C#
SqlDataAdapter da = new SqlDataAdapter(“select * from customers;select * from orders;”,myConnection);
DataSet ds = new DataSet();
da.fill(ds,”customers”);
如上面代码所示,customers表数据将会存放在一个命名为customers的DataTable中,而orders表数据将会放在一个命名为customers1的DataTable中。当然你也可以在数据填充结束后很容易地修改customers1表属性(TableName)为orders。然而,在以后的数据填充时,只会影响customers表中数据,而orders表将会忽略并同时创建一个新的命名为customers1的表。要解决这个问题,你就要在customers1和orders之间建立一个DataTableMapping映射。其他表也如此。举例说明:
‘Visual Basic
Dim da As SqlDataAdapter = New SqlDataAdapter(“SELECT * FROM Customers; SELECT * FROM Orders;”, myConnection)
da.TableMappings.Add(“Customers1”, “Orders”)
Dim ds As DataSet = New DataSet()
da.Fill(ds, “Customers”)
‘C#
SqlDataAdapter da = new SqlDataAdapter(“SELECT * FROM Customers; SELECT * FROM Orders;”, myConnection);
da.TableMappings.Add(“Customers1”, “Orders”);
DataSet ds = new DataSet();
da.Fill(ds, “Customers”);
Q.使用DataReader
下面是使用DataReader的一些技巧和一些问题的回答:
I.在使用相关Command访问任何输出参数之前必须关闭DataReader;
II.在读取数据结束后应当关闭DataReader。如果你的Connection仅仅是用来返回DataReader,那么在关闭DataReader之后你也应该立即关闭它。另外一个关闭Connection的方法是传递CommandBehavior.CloseConnection给ExecuteReader方法。此方法在你从一个方法中返回DataReader并且对这个DataReader没有关闭控制权或者关联的连接时使用时非常有用的。
III.DataReader是为已连接的数据存取设计;
IV.使用GetString、GetInt32等返回特殊数据类型数据;
V.一个连接只允许使用一个DataReader。在ADO中,如果你只创建一个连接并使用两个recordsets,一个只读游标和一个只进游标,但实际上,ADO已经为你创建了一个隐式的连接,并在不用的时候隐式地关闭它。ADO.NET中是不行的,你必须为每个DataReader创建一个Connection,这也是为了让你在使用Connection时给予更多的控制信息。
VI.默认下,DataReader每次读取时把行中所有的数据装载到内存中。并允许你随机存取当前行中数据。如果随即存取没有必要(没有必要把所有数据都装载到内存),并想提高执行效率,给ExecuteReader方法传递CommandBehavior.SequentialAccess,这样就会改变DataReader的默认动作为仅仅装载请求的数据到内存。注意的是这种方法要求你顺序地存取行中列数据,一旦你略过某一列,以后你将再不会读取到该列的数据。
VII.如果当你在完成从一个DataReader读取数据后,仍然还有大量未读不需要的数据,这就要在调用DataReader的Close命令之前调用Cancel命令。调用DataReader的Close命令会导致把不需要的数据装载进来并在关闭游标之前清空数据。而调用Cancel命令就会丢弃这部分数据,从而DataReader在关闭之前就不会读取它们。如果你正在从你的命令返回输出参数,调用Cancel命令同样会丢弃它们。如果你需要读取任何输出参数,你就不要使用Cancel,而直接使用Close。
R.BLOBs对象
当你使用DataReader读取二进制数据时,你应该传递CommandBehavior.SequentialAccess给ExecuteReader方法调用。因为DataReader的默认情况是每次读取数据时把每行中所有的数据都存储到内存,而二进制数据又非常的大,结果就会使大量的内存空间被一个单一的BLOB占用。SequentialAccess使得你的DataReader默认行为为仅读取需要的数据。然后你就可以使用GetBytes或者GetChars决定一次读取多少数据。
记住的是使用SequentialAccess后,你不能次序颠倒地访问DataReader中不同的字段。就是说,如果你的查询返回三列,其中第三列是BLOB数据类型,如果你想访问第三列数据,那么你就必须先访问第一列,然后是第二列,再然后才是第三列的BLOB数据。这是因为此时返回的数据是有序的,而且一旦你跳过某一列,再回过头来读取这一列是不行的。
S.使用命令
ADO.NET提供了执行命令的几种不同方法,同时也提供了优化执行命令的几种不同参数。下面将要介绍的是选择最佳执行命令的技巧和改善一个可执行命令的性能。
I.OleDbCommand最佳实践
.NET 框架中各种数据提供者之间的执行命令标准几乎是一样的。但是也有不同,下面是执行OleDbCommand的一些技巧:
①使用CommandType.Text调用存储过程,使用CommandType.StoredProcedure生成;
②确定设置OleDbParameter的类型、大小(如果要求)和精度(如果是数字或者小数),注意的是,如果你不明确设置OleDbParameter,OleDbCommand将会为你的执行命令重新生成OleDbParameter。
II.SqlCommand最佳实践
使用SqlCommand快速执行存储过程:如果你要调用一个存储过程,指定SqlCommand的CommandType为存储过程的CommandType,
这样在执行命令时,就会提交此命令是调用存储过程,从而达到快速执行。
III.使用已准备的方法
Command.Prepare方法优化你的参数化执行命令。Prepare结构为多重调用最优化指定命令。要使用Prepare,你首先得理解你的数据库是怎样相应Prepare调用。SQL Server 2000中,Command已经被隐式优化和Prepare不是必须的;在SQL Server7.0或其它数据库中使用Prepare是有效的。
IV.明确地指定计划和元数据
ADO.NET中的很多对象都要推断元数据信息,只要用户不指定它,举例如下:
①如果在DataSet中不存在,DataAdapter.Fill方法就会创建表和列信息;
②CommandBuilder为独立表的Select命令生成DataAdpater命令参数;
③CommandBuilder.DeriveParameters组装一个命令对象的参数信息;
如果什么时候都使用上面讲的方法,可能会降低执行性能。推荐在设计阶段和广告段应用程序中使用。可能的情况下,一般都要指定计划和元数据。这些包括指定DataSet的表和列、指定DataAdapter的Command属性和指定Command的参数信息。
V.ExecuteScalar和ExecuteNonQuery
如果你想只返回一个简单值,比如Count(*)、Sum(Price)或者Avg(Quantity),你可以使用ExecuteScalar,它帮助你一步到位得到你想要的值,从而避免使用DataReader的两步计算(ExecuteReader+GetValue);
当你不想返回行信息,比如修改数据(INSERT、UPDATE、DELETE)或者仅需要输出参数或者返回值,使用ExecuteNonQuery,它去掉不必要的处理创建一个空的DataReader。
VI.空值检查
如果在你的表中某列允许空值,你可以使用Where语句进行空值检查,下面举例说明:
select * from customers where ((LastName=@LastName) or (LastName IS NULL and @LastName IS NULL))
上面语句检查了列是否为空和参数是否为空。
VII.传递null参数值
当你在命令中传递null参数值给数据库时,你不能使用null(Nothing在vb中),应该使用DBNull.Value。举例:
‘vb
Dim param As SqlParameter = New SqlParameter(“@Name”,SqlDbType.NVarChar,20)
param.Value = DBNull.Value
‘C#
SqlParameter param = new SqlParameter(“@Name”,SqlDbType.NVarChar,20);
param.Value = DBNull.Value;
VIII.使用事务处理
ADO.NET中的事务处理模型已经改变,在ADO中,一旦StartTransaction被调用,任何事务下的更新都被认为是事务的一部分。然而,在ADO.NET中,当Connection.BeginTransaction被调用,返回一个命令关联的事务对象(事务属性是由命令的事务属性指定的)。这样保证让你在一个Connection中执行多个事务。如果命令的Command.Transaction属性与开始的事务不一致,命令就不会完成并抛出错误。
IX.使用Connections
高效率的应用程序应该使用最少的时间与数据库建立连接,比如使用Connection Pooling等。下面将介绍如何使用ADO.NET建立高效率应用的一些数据库方面的技巧。
①Connection Pooling
在SQL Server、OLE DB和.NET框架结构中的Data Provider中,都提供了隐式的连接池连接支持。你可以在ConnectionString中指定不同的参数值控制连接池的行为。比如下面的例子使OLE DB的连接池无效并自动地进行事务处理:
Provider=SQLOLEDB;OLE DB Services=-4;Data Source=localhost;Integrated Security=SSPI;
在SQL Server.NET Data Provider中提供了以下参数设置控制连接池的行为:Connection Lifttime、Connection Reset、Enlist、Max Pool Size、Min Pool Size和Pooling。
②使用DataAdapter最优化连接
使用DataAdpater的Fill和Update方法时会自动地打开相应的连接。如果Fill或者Update打开一个连接,在它操作完成后它会关闭此连接。最好的执行方式是在你需要的时候才建立连接。同时减少多个操作时打开和关闭连接的次数。
推荐你在仅仅执行一个Fill或者Update时,允许Fill或者Update方法隐式地打开和关闭连接;如果你要执行多个Fill或者Update,建议你显式地建立连接、执行Fill或者Update操作然后显式地关闭连接。
额外地,在我们执行事务处理时,在开始事务之前应该显式地建立连接,并在事务结束后显式地关闭连接。举例:
‘Visual Basic
Public Sub RunSqlTransaction(da As SqlDataAdapter, myConnection As SqlConnection, ds As DataSet)
myConnection.Open()
Dim myTrans As SqlTransaction = myConnection.BeginTransaction()
myCommand.Transaction = myTrans
Try
da.Update(ds)
myTrans.Commit()
Console.WriteLine(“Update successful.”)
Catch e As Exception
Try
myTrans.Rollback()
Catch ex As SqlException
If Not myTrans.Connection Is Nothing Then
Console.WriteLine(“An exception of type ” & ex.GetType().ToString() & _
“ was encountered while attempting to roll back the transaction.”)
End If
End Try
Console.WriteLine(“An exception of type ” & e.GetType().ToString() & “ was encountered.”)
Console.WriteLine(“Update failed.”)
End Try
myConnection.Close()
End Sub
‘C#
public void RunSqlTransaction(SqlDataAdapter da, SqlConnection myConnection, DataSet ds)
{
myConnection.Open();
SqlTransaction myTrans = myConnection.BeginTransaction();
myCommand.Transaction = myTrans;
try
{
da.Update(ds);
myCommand.Transaction.Commit();
Console.WriteLine(“Update successful.”);
}
catch(Exception e)
{
try
{
myTrans.Rollback();
}
catch (SqlException ex)
{
if (myTrans.Connection != null)
{
Console.WriteLine(“An exception of type ” + ex.GetType() +
“ was encountered while attempting to roll back the transaction.”);
}
}
Console.WriteLine(e.ToString());
Console.WriteLine(“Update failed.”);
}
myConnection.Close();
}
X.总是关闭Connections和DataReaders
在你使用完Connection或者DataReader对象后,你应该明确地关闭它们。系统中的碎片整理程序只是在最后需要的时候才进行整理,而一些很耗资源的连接还得由你自己来释放。同时如果你不明确地关闭连接,此连接就有可能不返回连接池,除非连接池的Max Pool Size已经达到并且此连接还仍然有效。
注意:在你的类的Finalize方法中不要使用Close或者Dispose运用到一个Connection或者一个DataReader或者任何被管理对象上。在一个Finalizer中,仅仅释放你的类直接拥有的无法管理的资源。如果你的类不拥有任何无法管理的资源,就不要在你的类使用Finalize方法。
XI.在C#中使用Using声明
在C#中,一个非常便利的保证关闭你使用过的Connection和DataReader对象的方法是使用Using声明。当对象超出了它的使用范围,Using声明就会自动地释放该对象。举例:
‘C#
string connString = “Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;”;
using (SqlConnection conn = new SqlConnection(connString))
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = “SELECT CustomerId, CompanyName FROM Customers”;
conn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
Console.WriteLine(“{0}\t{1}”, dr.GetString(0), dr.GetString(1));
}
}
Using声明在Visual Basic.Net中不可用。
XII.避免访问OleDbConnection.State属性
如果你需要经常检查State属性,最好在OleDbConnection上监听StateChange事件。下面的代码演示当OleDbConnection.State发生变化时使用StateChange向控制台发送一条消息:
‘Visual Basic
AddHandler nwindConn.StateChange, New StateChangeEventHandler(AddressOf OnStateChange)
Protected Shared Sub OnStateChange(sender As Object, args As StateChangeEventArgs)
Console.WriteLine(“The current Connection state has changed from {0} to {1}.”, _
args.OriginalState, args.CurrentState)
End Sub
‘C#
nwindConn.StateChange += new StateChangeEventHandler(OnStateChange);
protected static void OnStateChange(object sender, StateChangeEventArgs args)
{
Console.WriteLine(“The current Connection state has changed from {0} to {1}.”,
args.OriginalState, args.CurrentState);
}
ADO.NET最佳实践(下)
www.csdn.net/Develop/read_article.asp?id=22664
篇8:ADO.net中数据库连接方式数据库教程
ado|数据|数据库|数据库连接
在MSDN中,.net的数据库连接字符串都有详细的说明,我这里以代码范例的方式罗列一些,具体的每一项代表的意义可以参看MSDN.
ADO.net 中数据库连接方式(微软提供)
微软提供了以下四种数据库连接方式:
System.Data.OleDb.OleDbConnection
System.Data.SqlClient.SqlConnection
System.Data.Odbc.OdbcConnection
System.Data.OracleClient.OracleConnection
下面我们以范例的方式,来依次说明:
System.Data.SqlClient.SqlConnection
常用的一些连接字符串(C#代码):
SqlConnection conn
= new SqlConnection( “Server=(local);Integrated Security=SSPI;database=Pubs”);
SqlConnection conn
= new SqlConnection(“server=(local)\\NetSDK;database=pubs;Integrated Security=SSPI”);
SqlConnection conn = new SqlConnection(
“Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;”);
SqlConnection conn = new SqlConnection(
“ data source=(local);initial catalog=xr;integrated security=SSPI;
persist security info=False;workstation id=XURUI;packet size=4096; ”);
SqlConnection myConn = new
System.Data.SqlClient.SqlConnection(“Persist Security Info=False;Integrated
Security=SSPI;database=northwind;server=mySQLServer”);
SqlConnection conn = new SqlConnection(
“ uid=sa;pwd=passwords;initial catalog=pubs;data source=127.0.0.1;Connect Timeout=900”);
更多字符串连接说明请看MSDN:
msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemDataSqlClientSqlConnectionClassConnectionStringTopic.asp
System.Data.OleDb.OleDbConnection
常用的一些连接字符串(C#代码):
OleDbConnection conn = new OleDbConnection(@“Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\MyWeb\81\05\GrocerToGo.mdb”);
OleDbConnection conn = new OleDbConnection(
@“Provider=Microsoft.Jet.OLEDB.4.0;Password=;
User ID=Admin;Data Source=grocertogo.mdb;”);
OleDbConnection conn = new OleDbConnection(
“Provider=MSDAORA; Data Source=ORACLE8i7;Persist Security Info=False;Integrated Security=yes”);
OleDbConnection conn = new OleDbConnection(
“Provider=Microsoft.Jet.OLEDB.4.0; Data Source=c:\bin\LocalAccess40.mdb”);
OleDbConnection conn = new OleDbConnection(
“Provider=SQLOLEDB;Data Source=MySQLServer;Integrated Security=SSPI”);
更多字符串连接说明请看MSDN:
msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemDataOleDbOleDbConnectionClassConnectionStringTopic.asp?frame=true
System.Data.OracleClient.OracleConnection
常用的一些连接字符串(C#代码):
OracleConnection myConn = new System.Data.OracleClient.OracleConnection(
“Data Source=Oracle8i;Integrated Security=yes”);
更多字符串连接说明请看MSDN:
msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemDataOracleClientOracleConnectionClassConnectionStringTopic.asp?frame=true
System.Data.Odbc.OdbcConnection
常用的一些连接字符串(C#代码):
OdbcConnection conn = new OdbcConnection(
“Driver={SQL Server};Server=MyServer;Trusted_Connection=yes;Database=Northwind;”);
OdbcConnection conn = new OdbcConnection(
“Driver={Microsoft ODBC for Oracle};Server=ORACLE8i7;
Persist Security Info=False;Trusted_Connection=yes”);
OdbcConnection conn = new OdbcConnection(
“Driver={Microsoft Access Driver (*.mdb)};DBQ=c:\bin\nwind.mdb”);
OdbcConnection conn = new OdbcConnection(
“Driver={Microsoft Excel Driver (*.xls)};DBQ=c:\bin\book1.xls”);
OdbcConnection conn = new OdbcConnection(
“Driver={Microsoft Text Driver (*.txt; *.csv)};DBQ=c:\bin”);
OdbcConnection conn = new OdbcConnection(“DSN=dsnname”);
更多字符串连接说明请看MSDN:
msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemDataOdbcOdbcConnectionClassConnectionStringTopic.asp?frame=true
其他厂商提供的数据库连接:
DB2Connection myConn = new IBM.Data.DB2.DB2Connection(
“DATABASE = SAMPLE;UID= ;”);
DB2Connection myConn = new IBM.Data.DB2.DB2Connection(“DATABASE = SAMPLE”);
BdpConnection myConn = new Borland.Data.Provider.BdpConnection(“assembly=Borl
and.Data.Mssql,Version=1.1.0.0,Culture=neutral,PublicKeyToken=91d62ebb5b0d1b1b;ve
ndorclient=sqloledb.dll;osauthentication=False;database=
e= ;provider=MSSQL”);
BdpConnection myConn = new Borland.Data.Provider.BdpConnection(“assembly=Borl
and.Data.Db2,Version=1.1.0.0,Culture=neutral,PublicKeyToken=91d62ebb5b0d1b1b;ve
ndorclient=db2cli.dll;database=
password=
;provider=DB2”);
Connection Pooling
在SQL Server、OLE DB和.NET框架结构中的Data Provider中,都提供了隐式的连接池连接支持,
ADO.net中数据库连接方式数据库教程
,
你可以在ConnectionString中指定不同的参数值控制连接池的行为。比如下面的例子使OLE DB的连接池无效并自动地进行事务处理:
Provider=SQLOLEDB;OLE DB Services=-4;Data Source=localhost;Integrated Security=SSPI;
在SQL Server.NET Data Provider中提供了以下参数设置控制连接池的行为:Connection Lifttime、Connection Reset、Enlist、Max Pool Size、Min Pool Size和Pooling。
更多数据库连接信息,以及非ADO.net的连接字符串可以参看:
www.connectionstrings.com/
--
篇9:解决Oracle被锁定的妙招数据库教程
一些性能方面的告警信息通过dba_outstanding_alerts表都能查到,被锁定的表如果长期得不到释放,在这里也能查到相关session的sid和serial#,
一些性能方面的告警信息通过dba_outstanding_alerts表都能查到,被锁定的表如果长期得不到释放,在这里也能查到相关session的sid和serial#。
select * from dba_outstanding_alerts
v$locked_object视图可以看到当前被锁定的对象,只有那些一直存在的session才有可能是被锁定的,因此不能简单匆忙的下结论,尤其是在RAC环境中,必须查看各个节点以发现相关的session在哪里。
select * from v$locked_object
有时候也可以根据对象名来查看锁的信息:
select sid,id1,type from v$lock where id1=(select object_id from dba_objects where object_name=upper('mytablename'));
找到了引起锁定的session,就可以找到对应的sid和serial#
select saddr,sid,serial#,paddr,username,status from v$session where sid=772
从而可以杀死这个session:
SQL>alter system kill session '772,9044';
System altered.
可以查询Oracle数据库的进程和操作系统进程之间的关联:
select spid, osuser, s.program from v$session s,v$process p
where s.paddr=p.addr and s.sid=772
有时候一些进程要在操作系统杀死,这时可以用kill -9 pid的命令:
[root@erpdevdb ~]# ps -efgrep sqlplus
oracle 11847 11126 0 16:39 pts/1 00:00:00 sqlplus -S @/oracle/home/droptable.sql
root 11889 11856 0 16:40 pts/2 00:00:00 grep sqlplus
[root@erpdevdb ~]# kill -9 11847
数据库对象锁定引起的阻塞是比较麻烦的事情,处理的时候一定要小心谨慎,
★ 学习计算机技巧
★ c语言学习总结
★ ACCESS数据库中Field对象的caption属性读写数据库教程
【PowerDesign9.5+ 中的GTL编程 解决大问题数据库教程(精选9篇)】相关文章:
单片机编程心得体会2024-02-11
python基础教程之基本内置数据类型介绍2022-08-15
计算机编程怎么学习2022-07-14
C++基础教程数组2022-12-09
Python教学心得2022-11-03
Lua数据类型介绍2022-08-18
Lua教程(十七):C API简介2022-11-11
初学者怎么学习单片机2022-12-01
大学生计算机专业开题报告2022-05-06
外婆学习电脑作文2023-07-19