博客升级,打不开,不知道什么原因,
是不是上一篇太长了,于是,我只要分割它到下个了。
接上一篇:
上篇提到 一个CommandBuilder,这是一个新对象。
它的作用就是:自动生成单表命令,用于将对 DataSet 所做的更改与关联的 SQL Server 数据库的更改相协调
因为:da.update()更新命令并“不能”生成 实现DataSet和数据库协调的SQL语句.
说白了,DataAdapter只是一个无主意的搬运工,用怎么样的SQl语句用到数据库,需要一个“指示”或“指令”
而这个指令或指示就是由CommmandBuilder来说明的,这样搬运工就知道怎么搬进数据库,怎么放好放好(修改)数据。
CommandBuilder具有智能的作用:会自动生成相关的SQL语句。所以前面没看到Insert插入SQL语句。因为里面有了
newrow,addrow就可以判断是插入记录。
但是,如果设置了 SqlDataAdapter 的 SelectCommand 属性,则可以创建一个 SqlCommandBuilder 对象来自动生成用于
单表更新的 Transact-SQL 语句。然后,SqlCommandBuilder 将生成其他任何未设置的 Transact-SQL 语句。
每当设置了 DataAdapter 属性,SqlCommandBuilder 就将其本身注册为 RowUpdating 事件的侦听器。一次只能将一个
SqlDataAdapter 与一个 SqlCommandBuilder 对象(或相反)互相关联。
为了生成 INSERT、UPDATE 或 DELETE 语句,SqlCommandBuilder 会自动使用 SelectCommand 属性来检索所需的元数据集。
如果在检索到元数据后(例如在第一次更新后)更改 SelectCommand,则应调用 RefreshSchema 方法来更新元数据。
SelectCommand 还必须至少返回一个主键列或唯一的列。如果什么都没有返回,就会产生 InvalidOperation 异常,不生成命令。
SqlCommandBuilder 还使用由 SelectCommand 引用的 Connection、CommandTimeout 和 Transaction 属性。如果修改了
这些属性中的一个或多个,或者替换了 SelectCommand 本身,用户则应调用 RefreshSchema。否则,InsertCommand、
UpdateCommand 和 DeleteCommand 属性都保留它们以前的值。
如果调用 Dispose,则会解除 SqlCommandBuilder 与 SqlDataAdapter 的关联,并且不再使用生成的命令。
界面基本一下,就是按键代码变换一下。
需要说明的是:DataAdapter是一个呆板的搬运工,当搬运到内存中的DataSet的表是没有主键设置的,所以要操作时需要设置。
另外一个是,要定位到一个Row中,用一个Find()方法。
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim cn As New SqlConnection("Data Source=(local);Initial Catalog=Sales;Integrated Security=False;User ID=sa;Password=123456;") Dim da As New SqlDataAdapter("select * from grade", cn) '搬运工拉好水 Dim ds As New DataSet() '本地内存准备好容器来装水 da.Fill(ds, "mytable") '装水 Dim drow As DataRow '定义行变量 ds.Tables("mytable").PrimaryKey = New DataColumn() {ds.Tables("mytable").Columns("学号")} '设置内存表中的主键 drow = ds.Tables("mytable").Rows.Find(TextBox1.Text) '根据学号定准到对应行 Try drow("学号") = TextBox1.Text '对应行赋值 drow("姓名") = TextBox2.Text drow("语文") = TextBox3.Text drow("数学") = TextBox4.Text drow("英语") = TextBox5.Text Dim cmdb As New SqlCommandBuilder(da) '和数据库打个电话,本地内存有水要运过去 da.Update(ds, "mytable") '上面电话里已经说好了,现在把水运到数据库去 DataGridView1.DataSource = ds.Tables("mytable") '绑定到控件上显示当前结果 Catch ex As Exception MessageBox.Show(ex.ToString) End Try End Sub
注1:Rows.Find(object),默认提查找主键,当是主键是一个字段时,直接用这个字段。
注2:Rows.Find(object[]),主键是多个字段组成,则用几个字段来组成。用数组来表示,每个元素一个字段名,参数用数组名。
DataAdapter对象通过4个属性传递数据。
这四个属性必须是Command对象:SelectCommand,InsertCommand,UpdateCommand,DeleteCommand四个对象
如果是查询则是SelectCommand
如果是非查询是其余三个:插入、更新、删除
下例中就是da.SelectCommand,因为SQL是select,
Imports System.Data.SqlClient Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim cn As New SqlConnection("Data Source=.;Initial Catalog=Sales;Integrated Security=False;User ID=sa;Password=123456;") Dim cmd As New SqlCommand("select * from grade", cn) Dim da As New SqlDataAdapter da.SelectCommand = cmd cn.Open() Dim ds As New DataSet da.Fill(ds, "grade") DataGridView1.DataSource = ds.Tables("grade") End Sub End Class
注1:默认下,Connection的OPen()方法,DataAdapter自动调用SelectCommand属性。
除了SelectCommand属性,其它3个需要使用ExecuteNonQuery()方法调用。
注2:例中没有用cn.open,这是因为Fill有自动打开连接的功能。
Fill 方法使用 SELECT 语句从数据源中检索数据。与 Select 命令关联的 IDbConnection 对象必须有效,但不需要将其
打开。如果调用 Fill 之前 IDbConnection 已关闭,则将其打开以检索数据,然后再将其关闭。如果调用 Fill 之前连接
已打开,它将保持打开状态。
如果命令不返回任何行,则不向 DataSet 中添加表,并且不引发异常。
如果 DbDataAdapter 对象在填充 DataTable 时遇到重复列,它将以“columnname1”、“columnname2”、“columnname3”
这样依次排序的模式命名后面的列。如果传入数据包含未命名的列,它们将按“Column1”、“Column2”的模式放在 DataSet 中。
当指定的查询返回多项结果时,每个结果集都放置在单独的表中。将整数值追加到指定的表名从而对其他结果集进行
命名(例如“Table”、“Table1”、“Table2”等)。如果某个查询不返回行,则不会为该查询创建表,因此,如果您先处理一个
插入查询,然后再处理一个选择查询,那么由于为选择查询创建的表是第一个表,所以该表将被命名为“Table”。使用列名和
表名的应用程序应确保一定不要与这些命名模式发生冲突。
Fill 方法支持以下情况:DataSet 包含多个 DataTable 对象,而这些对象的名称只有大小写不同。在这种情况下,Fill 执行区分
大小写的比较以查找相应的表,如果不存在完全匹配的表,则新建一个。
例子:上面DataSet中,用DataAdapter来填充一个表,下面用来填充几个表。
注意每个表使用的Command对象,和实现过程。
对于ExecuteNonQuery必须是先有连接打开(cn.open),而selectCommand不必这样,所以cn.open在第二个之前,必须存在。
Imports System.Data.SqlClient Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim cn As New SqlConnection("Data Source=.;Initial Catalog=Sales;Integrated Security=False;User ID=sa;Password=123456;") Dim Insertcmd As New SqlCommand("insert into grade(学号,姓名,数学) values(111,'盖茨',59)", cn) Dim Selectcmd As New SqlCommand("select * from grade where 学号=111", cn) Dim Updatecmd As New SqlCommand("update grade set 数学=60 where 学号=111", cn) Dim da As New SqlDataAdapter da.SelectCommand = Selectcmd da.UpdateCommand = Updatecmd da.InsertCommand = Insertcmd Dim ds As New DataSet da.Fill(ds, "grade1") cn.Open() da.InsertCommand.ExecuteNonQuery() da.Fill(ds, "grade2") da.UpdateCommand.ExecuteNonQuery() da.Fill(ds, "grade3") DataGridView1.DataSource = ds.Tables("grade1") DataGridView2.DataSource = ds.Tables("grade2") DataGridView3.DataSource = ds.Tables("grade3") End Sub End Class
品味里面的cn.open位置。第一个fill没有打开连接,所以fill会自动连接,获取数据集后,再关闭。
第二和第三个fill因连接打开了,所以fill进,直接获取数据集,最后保持前面的状态,即保持连接。
所以有时为了性能,最好从开始一直打开,到最后得到结果后,再关闭。
注1: 第一个表没记录,因为没查出这个记录。第一个Fill默认用的是SelectCommand。
第二个只有一个记录,用的Insert,返回的是影响的记录,影响的是一条,所以显示一条
第三表只有一个记录,用的Update,返回影响的记录,影响仍是一条,所以显示一条。
注2:在填充(多个表)在一个DataSet中时
如果在填充数据表时遇到错误或异常,则错误发生之前添加的行将保留在数据表中。操作的剩余部分被中止。
当用于填充 DataSet 的 SELECT 语句(例如批处理 SQL 语句)返回多项结果时,请注意以下事项: •
如果其中一项结果包含错误,则将跳过所有后面的结果而不将其添加到 DataSet 中。
当使用后面的 Fill 调用来刷新 DataSet 的内容时,必须满足以下两个条件:
1.该 SQL 语句应该与最初用来填充 DataSet 的语句匹配。
2.必须存在键列信息。如果主键信息存在,则协调任何重复的行,并且这些重复行将只在与 DataSet 对应的
DataTable 中出现一次。可以通过 FillSchema(通过指定 DataTable 的 PrimaryKey 属性),
或者通过将 MissingSchemaAction 属性设置为 AddWithKey 来设置主键信息。
如果 SelectCommand 返回 OUTER JOIN 的结果,则 DataAdapter 不为生成的 DataTable 设置 PrimaryKey 值。必须显式
定义主键,确保正确地解析重复行。(前面有例子设置主键的情况)
水厂(数据库)<--------搬运工(DataAdapter)fill----------->用户家中水缸(DataSet)
水缸的作用就是离线技术,它独立于数据库。用户可以任意操作它,必要时,还可使用搬运工返回水厂(现实中是不行的)
DataSet相当于用户本机内存中的数据库,它采用XML描述,所以可以描述复杂关系。
DataSet还可以装很多个表,还有可以装表间的关系、约束等。它提供了一致的关系编程模型。
DataSet的重要对象如下:
1、TableCollection对象:即DataTable,可含多个,每个DataTable都含一个ColumnsCollection和一个RowsCollection,所以不用New它。
2、RelationsCollection对象:各DataTable之间关系通过DataRelation来表达,它的集合就是RelationsCollection对象。
3、ExtendedProperties对象:用来定义特定的信息,比如密码、更新时间等。
总之,DataSet是用户手中的“数据库”,可以任意来操作它,其表名也是可以自己定义的,同时它还可以再次返回到原来服务器上的数据库 中。
水缸----》里面有很多分格的小水缸(表)----》小水缸中有行集合和列集体。
由行-->行集合 ,添加到表集合--->再添加到水缸中。
Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim ds As New DataSet '建立水缸 Dim dt As New DataTable("Squ") '建立水缸中的分格容器即表,表名:Squ Dim drow As DataRow '不能new,因为分格容器中含一个列和一个行对象 dt.Columns.Add(New DataColumn("数字", GetType(Integer))) '添加一列 dt.Columns.Add(New DataColumn("平方", GetType(Integer))) Dim i As Integer For i = 3 To 10 drow = dt.NewRow '表中新增空行,下面赋值 drow("数字") = i '或drow(0) drow(1) = i * i dt.Rows.Add(drow) '新行进入表中 Next ds.Tables.Add(dt) '把这个已经有“内容”的表,填充到水缸中 DataGridView1.DataSource = ds.Tables("Squ") End Sub End Class
注:上例 最后添加一个保存到文件的代码:
ds.WriteXml("D:\Squ.xml")
即把DataSet保存到XML文件中,打开这个文件如下:
对于DataSet中的表,也可以用视图方式,视图DataView对象
在DataSet的DataTable上建立DataView。
DataView对象定义了DataTable的数据查看方式。数据默认的查看方式 是将数据以表格的形式排列,
且数据排列的顺序遵循从数据库表中取出数据时排列的顺序,通过DataView对象合数据按照某些规则显示。
Imports System.Data.SqlClient Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim cn As New SqlConnection("Data Source=.;Initial Catalog=Sales;Integrated Security=False;uid=sa;Password=123456;") Dim cmd As New SqlCommand("select * from grade", cn) Dim da As New SqlDataAdapter da.SelectCommand = cmd Dim ds As New DataSet da.Fill(ds, "grade") Dim dv As New DataView(ds.Tables("grade")) dv.RowFilter = "数学>60" dv.Sort = "数学 desc,英语" DataGridView1.DataSource = dv cn.Close() End Sub End Class
过滤条件由RowFilter指出,排列顺序由Sort指出。
DataView是本地内存中的视图,所以效率非常高,查询、排序等直接在内存中进行,不用与数据库进行数据交换。
DataView的构造函数有三个:
1、无参,空的视图
2、带一个参数的,就上例,直接带表集合;
3、带四个参数,即DataView(table As DataTable, RowFilter As String, Sort As String,RowState As DataViewRowState)
前三个分别为表集合、过滤条件、排序,形式如上例所表。最后一个是RowState。
最后一个是行的状态的过滤。比如,指明是新加入的行,则表中为新加入的行;如果指明是修改的行,则过滤为修改过的行。
几个值:CurrentRows 当前所有行(含未修改、新加入、已经修改的行)
Added 新加入的行
ModifiedCurrent、 ModifiedOriginal 修改的行(前一个表示动过,后一个表示相对最原始数据有改变)
(比如原行数据是35,修改成34,再改回去35,则表示动过,但相对原始数据没修改)
这个几个值可以用and,or等进行合并等
Imports System.Data.SqlClient Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim cn As New SqlConnection("Data Source=.;Initial Catalog=Sales;Integrated Security=False;uid=sa;Password=123456;") Dim cmd As New SqlCommand("select * from grade", cn) Dim da As New SqlDataAdapter da.SelectCommand = cmd Dim ds As New DataSet da.Fill(ds, "grade") Dim dv As New DataView(ds.Tables("grade"), "", "姓名", DataViewRowState.CurrentRows) '指定默认查找列为姓名 Dim rowIndex As Integer = dv.Find(TextBox1.Text) If rowIndex = -1 Then Label1.Text = "没相关记录" Else Label1.Text = dv(rowIndex)("姓名").ToString & " " & dv(rowIndex)("语文").ToString & " " & dv(rowIndex)("数学").ToString End If cn.Close() End Sub End Class
5个关键对象已经完毕。
基本上前面的重复。有些细节可能新的。
唯一注意有一个事务:开始、提交、回滚,新东西,不过个东西,好像用得不多。