2 获取数据
连接数据库之后,就可以从数据库中提取数据了。在示例程序中,点击“从数据库获取数据”按钮,可以看到状态栏上的提示信息——“数据已取回,共 324 条”。
图 5 ? 10 获取数据
提取数据也是个较复杂的过程,需要有好几个对象通力协作才能完成。
( 1 )定义一个 OleDbDataAdapter 对象,这个对象负责从数据库中提取数据:
Private da As OleDbDataAdapter = Nothing
( 2 )定义一个 OleDbCommand 对象,此对象负责向数据库发送 SQL 命令:
Private comm As OleDbCommand = Nothing
( 3 )定义一个 DataTable 对象,此对象用于存放数据,相当于数据库中的表。
Private dt As DataTable
这三个对象加上前面介绍的 OleDbConnection 对象,就可以实现数据的提取。这是由 Sub 过程 OnGetData() 实现的:
1 Private Sub OnGetData()
2 If da Is Nothing Then
3 da = New OleDbDataAdapter
4 End If
5
6 If comm Is Nothing Then
7 comm = New OleDbCommand
8 comm.Connection = conn ' 指定连接对象
9 comm.CommandType = CommandType.Text ' 指明使用 SQL 命令
10 comm.CommandText = "Select * from Client" ' 指定 SQL 命令内容
11 End If
12 ' 将 OleDbCommand 对象与 OleDbDataAdapter 对连接起来。
13 da.SelectCommand = comm
14
15 If dt Is Nothing Then
16 ' 创建一个 DataTable 对象 , 存放从数据库中取出来的数据
17 dt = New DataTable
18 End If
19
20 '填充数据
21 da.Fill(dt)
22
23 '激活 “ 在网格中显示记录 ” 按钮
24 Me .btnShowDataInGrid.Enabled = True
25
26 '显示信息
27 Me .StatusBar1.Text = "数据已取回,共" & dt.Rows.Count & "条"
28
29 End Sub
注意第 8 句,第 13 句和第 21 句,这三条语句实现了对象之间的合作。
第 8 句的含义是 OleDbCommand 对象采用 OleDbConnection 对象连接数据库,通过这一连接向数据库发送 SQL 命令。
第 13 句表明 OleDbAdapter 对象使用 OleDbCommand 对象向数据库发送提取数据的“ Select … ”命令。
第 21 句表明 OleDbAdapter 对象将取回的数据放到 DataTable 对象中。
这四个对象之间的关系可以用以下图进行说明:
图 5 ? 11 ADO.NET 对象的合作关系
图 5 ? 11 中,程序向数据库发送的各种命令(比如连接数据库,执行 SQL 命令等)被称为“命令流”,可以看到命令主要由 OleDbAdapter 负责确定,它委托 OleDbCommand 对象执行此特定的命令,而 OleDbCommand 需要通过 OleDbConnection 对象与数据库连接上之后,再把此命令传给数据库本身,由数据库管理系统( DBMS )执行此命令。
如果被执行的命令是一条提取数据的命令,则数据库会把需要的数据准备好,通过 OleDbConnection 对象建立的连接传给 OleDbAdapter ,由它负责把数据“塞入” DataTable 对象中。被传送的数据在 图 5 ? 11 中被称为“数据流”,以虚线表示。传送数据的终点站是 DataTable 中,而 OleDbAdapter 可以看成是一个数据的中转站,其 Fill 方法将收到的数据再转给 DataTable 对象。一旦数据从数据库“搬家”到 DataTable 之后,这些数据就自立门户,与数据库“老家”中的数据没有任何联系了。
** ** ** 提示: **
此处只是简单介绍了一种最常见的数据流动情况,更完整详细的介绍请参见第七章。
3 显示记录
数据到达 DataTable 之后,并不能自动地显示在窗体上,这是因为 DataTable 是个非可视的对象。
在示例程序中点击“在网格中显示记录”,运行结果如图 5 ? 12 所示。
图 5 ? 12 显示数据
将 DataTable 中的数据显示在可视化控件 DataGrid 中是通过以下代码实现的:
DataGrid1.DataSource = dt
通过设定 DataGrid 对象的 DataSource 属性为 DataTable, 就在 DataTable 与 DataGrid 中建立了一种关联, DataTable 中数据的任何变化都会同步地在 DataGrid 中显示,同样的,用户也可以通过 DataGrid 来直接修改数据,他所做的所有修改都会被保存到 DataTable 中。
有个专门的术语用于描述 DataTable 与 DataGrid 之间建立的这种关联,这就是“数据绑定 (DataBinding) ”。
数据绑定是个非常重要的概念,将在 7.5 节中对这一概念的真正含义作完整的解释。
4 编辑与修改记录
任何一个数据库应用程序,如果只能显示数据而不能增删改数据,都是用途有限的。 .NET 所提供的 DataGrid 是一个功能强大的数据绑定控件,直接支持对记录的增加、删除与修改。
图 5 ? 13 修改
如图 5 ? 13 所示,在网格中点击某个单元格,就可以直接修改这一字段的内容,并在当前行左端显示一支小铅笔图标。
直接点击最后一行的任意单元格,输入新值,就可以新增记录,如图 5 ? 14 所示:
图 5 ? 14 新增记录
点击最左边的一列,可以选中一行,再按键盘上的“ DEL ”键,可以删除记录。
图 5 ? 15 选中一行记录删除
虽然通过 DataGrid 可以对记录进行增、删、改,但如果这时直接关闭程序,当再次运行程序时,会发现所做的修改都没有被保存。其原因何在?
修改不能保存的最根本原因是数据一旦从数据库中取出,这些数据独立于数据库了,换言之,取出的数据存放在内存中,对它的修改不会导致数据库的内容随之改变。这种数据独立性给我们带来了很大的灵活性。
** ** ** 注意: **
VB6 中使用 DataGrid 显示数据库中数据时,其增删改会直接保存到数据库中,即在任何时候,在窗体上看到的数据就是数据库中的数据(除非采用了“断开”方式)。 VB6 所使用的数据库技术是 ADO ,而 VB.NET 使用的是 ADO.NET ,两者有着根本的不同。 VB6 程序员要注意这个重要的区别。
但确实需要将修改后的数据永久保存,为此,可以使用 OLEDBDataAdapter 对象的 Update() 方法实现此功能。
** 试一试: **
在示例程序中,给“保存”按钮的单击事件加入下面一句:
' 将更改保存到数据库中
da.Update(dt)
然后运行程序,随意修改、删除或增加几条记录,单击“保存”按钮 。 观察运行情况。
可以看到,直接调用 Update() 方法时,会引发一个异常,如 图 5 ? 15 所示:
图 5 ? 16 更新异常
其原因何在?
数据的提取与保存都是由 OLEDBDataAdapter 负责的,它通过四个 OLEDBCommand 对象类型的属性来完成四种典型的数据处理任务:
提取记录: SelectCommand
新增记录: InsertCommand
删除记录: DeleteCommand
修改记录: UpdateCommand
当我们从数据库中提取记录时,已向 OLEDBDataAdapter 传入了一个 OLEDBCommand 对象,其执行的 SQL 命令是:
Select * from Client
但我们并没有给其它三个操作提供有效的 OLEDBCommand 对象,所以,在示例程序中 OLEDBDataAdapter 无法保存数据。
OLEDBCommand 对象是专用于执行 SQL 命令的, InsertCommand 执行 SQL 中的 Insert 命令, DeleteCommand 执行 SQL 中的 Delete 命令 , UpdateCommand 执行 SQL 中的 Update 命令。
** ** ** 提示: **
在第六章中,将介绍 SQL 的常用命令,学会编写 SQL 语句完成许多数据处理工作。
可以手动地创建 OLEDBCommand 对象并设置其 SQL 命令,然后把它传给 OLEDBDataAdapter 对象,但这是一个麻烦且易出错的工作, .NET 提供了一个 OleDbCommandBuilder 类来完成此工作。参见以下代码:
Private cb As OleDbCommandBuilder
' 保存数据
Private Sub OnSaveData()
If cb Is Nothing Then
cb = New OleDbCommandBuilder
cb.DataAdapter = da
' 设定更新命令
da.UpdateCommand = cb.GetUpdateCommand
' 设定插入命令
da.InsertCommand = cb.GetInsertCommand
' <SPAN style="FONT-SIZE: 11pt; COLOR: green; FONT-FAMILY: 新宋体; mso-bidi-font-family: '