第14章 VB.NET数据库应用程序开发 数据层表示后台的数据库,主要完成数据的存储、操纵和管理功能; 图14-1 数据库应用系统的三层结构 数据层表示后台的数据库,主要完成数据的存储、操纵和管理功能; 表示层是前台的应用程序功能交互界面,为用户提供基于数据查询和数据操作的业务功能服务; 数据访问层ADO(ActiveX Data Object)介于数据层和表示层之间,主要负责数据层与表示层之间的数据访问和数据传输。。
14.1 ADO.NET 简介 VS.NET开发平台用ADO.NET组件来实施数据访问功能。支持断开连接模型,使用XML在程序间交换数据。开发的程序伸缩性好、高性能可维护性强。 ADO.NET 组件包括两个核心组件:数据集(Dataset)和.NET数据提供程序(Data Provider),将数据访问从数据处理中分离出来,以便更好地支持断开连接模型。 数据提供程序的功能: 建立DataSet 和数据库的连接,保持两者之间的数据一致性。 通过执行SQL命令或存储过程来操作数据。 Dataset对象用于在内存保存数据库数据。 独立于任何数据源的数据访问。
14.1.1 ADO.NET数据提供程序对象模型 ADO.NET数据提供程序用于进行数据库连接、执行命令和获取结果。这些结果即可以通过DataReader直接处理,也可以通过DataAdapter放在DataSet中,以特别的方式显示给用户,或者与来自多个源的数据相结合,或者在层间进行传递。 .NET提供了四个数据提供程序,每个都包含一个系列对象,分别为不同的数据源提供数据访问操作支持。 服务名称 支持主要服务和管理 Microsoft SQL Server .NET 数据提供程序 该提供程序使 .NET应用程序可以直接访问 Microsoft SQL Server 数据库。适用于Microsoft SQL Server 14.0 或更高版本 Microsoft OLE DB .NET 该提供程序使 .NET应用程序可以使用它们的本机OLE DB提供程序访问数据库 Microsoft ODBC .NET 该提供程序使 .NET应用程序可以通过使用它们的 ODBC 驱动程序访问数据库 Microsoft Oracle .NET 该提供程序使 .NET应用程序可以访问 Oracle 数据库 各个.NET 数据提供程序相关联的类型位于其各自的命名空间中,例如: System.Data.SqlClient包含 SQL Server .NET 数据提供程序类型。
Microsoft SQLServer.NET数据提供程序 SQLServer.NET数据提供程序使用Tabular Data Stream(TDS)和SQL Server通信。由于TDS是SQL Server的本机通信协议,并经过了优化,可以直接访问 SQL Server 而不用添加 OLE DB或开放式数据库连接(ODBC)层,因此它是轻量的,并具有良好性能。 测试发现,SQLServer.NET数据提供程序的执行速度大约比OLE DB.NET数据提供程序快70%(图14-3)。 图14-3 SQL Server .NET Framework 数据提供程序和 OLE DB .NET Framework 数据提供程序的比较 由于本书是以Microsoft SQL Server2000为数据库管理系统,所以程序实例都采用SQL Server.NET数据提供程序中的组件对象。
Initial Catalog= School; DataSource=myServer; User ID=sa; password=; 1.Connection对象 Connection对象用于在应用程序和数据库之间建立连接,要连接一个指定的数据源,它是应用程序访问数据库的第一步。 要连接Ms SQL Server需要使用SQL Server.NET数据提供程序的SQLConnection对象;要连接OLE DB数据源,需要使用OLE DB.NET数据提供程序的OleDbConnection对象。 (1)连接字符串格式—SqlConnection 属性 说明 Data Source 指定数据库服务器名称或位置 Initial Catalog 指定数据库名 User ID 用户帐户 Password 用户密码 例如:一个SqlConnection连接字符串的例子: Initial Catalog= School; DataSource=myServer; User ID=sa; password=; 如果是集成安全模式,SqlConnection连接字符串可简化为: Initial Catalog= School; DataSource=myServer; Integrated Security=SSPI;
2.Command对象 与数据源建立连接后,可以直接通过Command对象与数据源进行交互访问。它为ADO.NET的数据库数据访问带来了很大的灵活性。 3.DataReader对象 DataReader对象可以从数据库中获取一个只读、仅向前的数据流。使用DataReader可以提高应用程序的性能,减少系统开销,因为在内存中每次只能有一个缓冲行。 4.DataAdapter对象 DataAdapter对象主要是在数据源以及DataSet之间执行数据传输的工作。DataAdapter对象用于获得数据源中的数据,填充DataSets;DataAdapter还可以将DataSet产生的改变解析回数据源。它使用.NET数据提供程序中的Connection对象连接数据源,使用Command对象获取数据源中的数据并填充到DataSet对象中,并将DataSet对象中数据的变化写回数据源。
14.1.2 DataSet对象 在ADO.NET中,DataSet对象主要支持断开连接的、分布式数据的处理,类似于一个驻留在内存中的关系数据库,表示一个完整的数据集。数据集可以包含一个或多个表,这些表可以来源于一个数据库,也可以来源于多个数据库。除了包含表,数据集还可以包含说明表之间关系的对象的数据结构,即相关约束和表与表之间的联系。DataSet中的方法和对象与关系数据库模型中的对象和方法是一致的。 DataSet 包含一个或多个DataTable对象的集合,这些对象由数据行和数据列以及主键、外键、约束和有关DataTable对象中数据的关系信息组成。 每个DataTable对象中包含DataRow和DataColumn集合。DataRow中包含一个元组的所有信息,保留着原始数据和任何改变的数据;DataColumn表示属性列的信息。 DataRelation对象存储关系表之间的联系信息,包括主键和外键之间的对应关系。
14.1.3 ADO.NET数据库访问的一般过程 1. ADO.NET数据库访问的一般过程 1)创建SqlConnection对象,建立数据库连接。 2)通过SqlCommand创建数据适配器SqlDataAdapter,建立数据库和数据集之间的“桥”。 3)建立数据集DateSet。通过数据库与DataSet之间的桥SqlDataAdapter,编写程序代码用于填充数据集和更新数据集。即将数据库中的数据填充到内存数据集DataSet中;也可以通过SqlDataAdapter将更新过的DataSet中的数据回填到数据库中。 4)在VB.NET程序窗体上添加控件并设置其属性,使其和DataSet对象中的表和属性列相关联。应用程序针对DataSet进行数据检索和数据更新操作。
图14-5 工具箱中的SqlDataAdapter 【例14-1】在VB.NET项目中,创建一个和School数据库的数据连接,然后创建数据集对象获取T_Grade表中的所有数据,并在运行程序时通过窗体控件DataGrid显示出来。(通过数据适配器配置向导创建数据集,然后由窗体控件获取数据库数据的实例 ,运行结果窗体如图14-16所示。) 图14-16 学生选课信息 1)建立一个新的VB.NET项目文件,在项目设计器上,展开 【工具箱】中的【数据】选项卡,将SqlDataAdapter拖至窗体上,进入如图14-6所示的界面。 图14-7 数据链接属性对话框 图14-5 工具箱中的SqlDataAdapter 图14-6 数据适配器配置向导
4)单击【下一步】出现图14-12所示的对话框,说明数据适配器配置成功。 2)单击【下一步】,然后在出现的对话框中,单击【新建连接】将出现图14-7对话框。按照图示要求,填入要连接的服务器名(本机为Local)和数据库名School,并按【测试连接】。如果连接成功,将出现图14-8所示的对话框。 图14-8 数据库连接成功 3)单击图14-7窗口中的【确定】就进入图14-9所示的对话框,直接按【下一步】,在图14-10中选择“使用SQL语句”,单击【下一步】,出现图14-11所示的对话框,在文本框内写入下列SQL语句: SELECT * FROM T_Grade 图14-11 生成SQL语句对话框 图14-9 建立数据库连接成功 图14-10选择查询类型对话框 图14-12数据适配器配置成功 4)单击【下一步】出现图14-12所示的对话框,说明数据适配器配置成功。
图14-13生成数据库连接对象和数据适配器对象的窗体 5)在数据适配器配置成功后,生成了数据库连接对象SqlConnection1和数据适配器SqlDataAdapter1对象,操作界面回到VB.NET项目窗体,见图14-13。将鼠标移进窗体,单击右键,出现弹出式菜单,选择【生成数据集】,进入图14-14所示的对话框,数据集自动命名为DataSet1,单击【确定】,完成数据集DataSet1的创建。进入图14-15窗口,在解决方法资源管理器中出现DataSet1.xsd文件,在窗体下部组件栏中显示数据集对象实例DataSet11。 图14-13生成数据库连接对象和数据适配器对象的窗体 图14-14生成数据集
Private Sub Form1_Load(…) Handles MyBase.Load 6)创建好数据集后,就可以进行前台的数据访问操作。在窗体上,放置控件DataGrid1,选中DataGrid1,在其属性对话框中设置数据源DataSource为:DataSet11.T_grade。并设置窗体标题和说明标签“学生选课成绩”,出现图14-15所示的界面。 图14-15控件数据源的设定 7)填充数据集。数据集中的表数据要通过数据适配器的填充(Fill)方法获得。将该方法的调用放到Form1_Load事件过程中,这样启动程序后,即自动提取相关数据。代码为: Private Sub Form1_Load(…) Handles MyBase.Load SqlDataAdapter1.Fill(DataSet11, "T_Grade") End Sub 8)运行项目文件。
通过服务器资源管理器生成数据集DataSet对象 1)点击“视图/服务器资源管理器”菜单展开在服务器资源管理器,如图14-19所示,选择数据表“T_Major”并将其拖放到窗体上,这时会在窗体下的组件板上有新的SqlConnection1组件和SqlDataAdapter1对象。 2)右击组件板上的SqlDataAdapter1对象,选择生成数据集,会弹出类似图14-17的对话框,并单击【确定】按钮确认后就在组件板上生成了数据集实例DataSet11。
3.通过程序代码访问数据库 (1)对象及其成员访问 1)对象变量的声明 用程序编码实现数据库访问,需要通过代码定义SqlConnection,SqlDataAdapter和DataSet对象,及通过代码为DataGrid控件的数据源赋值。 (1)对象及其成员访问 对象类型数据需要先声明后使用。对象变量的赋值从形式上与普通变量相似,但实质上不同,对象变量的赋值是将对象实例的地址赋予对象变量。 1)对象变量的声明 声明形式:Dim 对象变量名 As [New] 类名[ (参数列表) ] 类名可以是预定义的类(如Form、TextBox、SqlConnection,SqlDataAdapter、DataSet等),也可以是用户自己定义的类。 [New] 类名[ (参数列表) ]用来创建类的实例,其中参数随着类的不同而不同。 例如:Dim a As New DataSet() '声明a为DataSet类的对象变量,并创建一个实例赋给a 该语句也可以写为: Dim a As DataSet '声明a为DataSet类的对象变量 a=New DataSet() '创建一个实例赋给a.
2)对象成员的访问 对象成员通过对象变量访问,其一般形式如下: 数据成员:对象变量.数据成员 属性:对象变量.属性 方法:对象变量.方法 事件:对象变量.事件 3)对象变量的释放 对象变量定义后,为其分配的内存空间较大,应该在不用的时候及时释放,以免影响系统中其他程序的运行效率。释放对象变量的语句形式为: 对象变量 = Nothing
(2)数据库访问程序中使用的对象 SqlConnection,SqlDataAdapter对象包含在Sql Server数据提供程序中,其命名空间为System.Data.SqlClient。DataSet对象的命名空间为System.Data。 要使用它们,需要在程序通过Imports语句指定其命名空间: Imports System.Data.SqlClient 这些对象的使用非常灵活,实现相同功能有多种不同的语句表达方法,下面仅做一些简要的介绍。 1)SqlConnection对象 声明和创建SqlConnection对象实例 SqlConnection对象用于在应用程序和数据库之间建立连接,声明并创建一个Connection对象实例的格式如下: Dim 对象变量名 As New SqlConnection(连接字符串) 连接字符串包括若干个连接参数,如表14-3所示。
1. SqlConnection默认使用SQL Server.NET数据提供程序,可省略Provider参数; 表14-3 SQL Server.NET数据提供程序Connection对象连接参数 属性 说明 Provider 数据提供程序名称 Data Source 需访问的数据库服务器名称或位置 Initial Catalog 需访问的数据库名 User ID 需访问的SQl Server数据库中的用户帐户 Password 需访问的SQl Server数据库中的用户帐户的密码 说明: 1. SqlConnection默认使用SQL Server.NET数据提供程序,可省略Provider参数; 2. DataSource可以赋值为数据库服务器的名称或IP地址,如果是本地服务器,DataSource也可以省略,也可以设置为“.”或“(local)”。 举例: 声明和创建SqlConnection对象实例cn,它使用用户“sa”(密码为空)连接本机SQL Server服务器中的School数据库: Dim cn As New SqlConnection("Initial Catalog=School; DataSource=.; User ID=sa; Password=;") 如果采用Windows集成安全模式,SqlConnection连接字符串可简化为: Dim cn As New SqlConnection("Initial Catalog=School; DataSource=.; Integrated Security=SSPI;")
ConnectionString:获取或设置用于打开 SQL Server 数据库的字符串。 SqlConnection的属性 ConnectionString:获取或设置用于打开 SQL Server 数据库的字符串。 ConnectionTimeout:获取或设置在尝试建立连接时终止尝试所等待的时间。 SqlConnection的方法 Open():使用 ConnectionString 所指定的属性设置打开数据库连接。 Close():关闭与数据库的连接。 采用以上属性和方法,创建一个SqlConnection对象实例也可用以下代码: Dim cn As New SqlConnection() cn.ConnectionString="Initial Catalog=School; DataSource=. ; Integrated Security=SSPI" cn.ConnectionTimeout=30 '尝试连接时间30秒 cn.Open '打开数据库连接
声明和创建SqlDataAdapter对象实例 SqlDataAdapter用于获得SqlConnection对象所连接的数据源中的数据,填充DataSet,并可将DataSet对象中数据的变化写回数据源。声明并创建一个SqlDataAdapter对象实例格式: Dim 对象变量名 As New SqlDataAdapter(命令字符串, SqlConnection对象变量) SqlConnection对象变量是已经创建的SqlConnection实例,该参数也可以直接用连接字符串替代,这样可以省去创建SqlConnection对象的步骤; 命令字符串是T_SQL的Select语句或存储过程。 例如:声明并创建一个SqlDataAdapter对象实例提取数据源cn变量中的成绩表(T_Grade)信息: Dim da As SqlDataAdapter = New SqlDataAdapter("Select * From T_Grade", cn)
SqlDataAdapter的属性 SelectCommand:获取或设置一个T-SQL语句或存储过程,数据源中选择记录。 InsertCommand:获取或设置一个T-SQL语句或存储过程,数据源中插入记录。 DeleteCommand:获取或设置一个T-SQL语句或存储过程,从数据集删除记录。 UpdateCommand:获取或设置一个T-SQL语句或存储过程,更新数据源中记录。 SqlDataAdapter的方法 SqlDataAdapter的Fill()方法用于向数据集中填充数据。通常有两种用法: Fill(DataSet, Table):在DataSet中添加或刷新记录以填充DataSet数据集和数据集中的表Table,如果Table不存在,会首先创建一个名为Table的DataTable对象。 Fill(DataSet.Table):在 DataSet中添加或刷新记录以填充DataSet数据集中的表Table,要求Table已存在。 SqlDataAdapter的Update()方法为DataSet 中执行的INSERT、UPDATE 或 DELETE等修改操作回写到数据集。 Update(DataSet) :将修改操作回写到数据集DataSet。 Update(DataTable): 将修改操作回写到数据集中的数据表DataTable。
声明和创建DataSet对象实例 DataSet的属性 3)DataSet对象 Dim 对象变量名 As NewDataSet() SqlDataAdapter将获取的数据填充到DataSet对象的表中。 Dim 对象变量名 As NewDataSet() 举例:声明并创建一个DataSet对象实例ds,并且采用SqlDataAdapter的Fill方法向其中填充数据,数据集ds中的数据表命名为“Grade”。 Dim ds As New DataSet() da.Fill(ds, "Grade") DataSet的属性 Tables:获取包含在 DataSet 中的表的集合。
【例14-2】通过程序实现例14-1的功能:在窗体上用DataGrid控件浏览学生选课成绩。 用程序编码实现数据库访问,需要通过代码定义SqlConnection,SqlDataAdapter和DataSet对象,及通过代码为DataGrid控件的数据源赋值。 Imports System.Data.SqlClient ‘指定SQL Server数据提供程序的命名空间 Private Sub Form1_Load(…) Handles MyBase.Load Dim cn As New SqlConnection("Initial Catalog=School; Data Source=(Local); " _ & "Integrated Security=SSPI;") '定义数据库连接对象SqlConnection实例cn Dim sql As String = "Select * From T_Grade" '定义Select命令语句sql访问数据表T_Grade Dim ad As SqlDataAdapter = New SqlDataAdapter(sql, cn) '定义SqlDataAdapter实例ad Dim ds As New DataSet() ‘定义DataSet对象实例da ad.Fill(ds, "Grade") '将数据适配器ad获得的查询结果填充到数据集ds和“Grade”表 DataGrid1.DataSource = ds.Tables("Grade") '为控件DataGrid1指定显示的数据源 End Sub 数据集中的表“Grade”不同于数据库中的“T_Grade”,因此它也可以另外命名。
【例14-3】查询给定学号的学生修课成绩。程序运行界面如图。 注意查询字符串的条件表达。学生学号在文本框TextBox1.Text中,在写该条件时一定要从查询串中分离出来,只有这样才能将学号这个条件从文本框提取出来,从而构造出按学号查询的SQL语句。 【例14-3】查询给定学号的学生修课成绩。程序运行界面如图。 Private Sub Button1_Click(…) Handles Button1.Click Dim cn As New SqlConnection("Initial Catalog=School; Data Source=(Local);" _ & "Integrated Security=SSPI; ") '定义数据库连接对象cn '定义满足要求的查询命令, 即查询指定学号的学生选课记录 Dim sql As String = "Select * From T_Grade Where StudentCode='" & TextBox1.Text & "'" Dim ad As SqlDataAdapter = New SqlDataAdapter(sql, cn) '定义数据适配器对象ad Dim ds As New DataSet() '定义数据集对象ds ad.Fill(ds, "Grade") '将满足条件的查询结果填充到数据集中的表Grade DataGrid1.DataSource = ds.Tables("Grade") '为控件 DataGrid1指定显示的数据源 End Sub
14.2 基于绑定的数据访问 .Net FrameWork SDK类库中有个基类System.Windows.Froms.BindingManagerBase, 它管理绑定到相同数据源和数据成员的所有 Binding对象,允许对Windows窗体上绑定到相同数据源的数据绑定控件进行同步。 数据绑定技术的核心: 借助绑定对象(程序界面控件)的数据指针浏览绑定数据源的信息。即通过定义绑定对象和设置其绑定的数据源,并且建立窗体控件和绑定数据源的属性对应关系,最后依据绑定对象的指针(Position),使界面上所有绑定控件访问数据源数据时可以保持步调一致。 14.2.1绑定对象BindingManagerBase 1.定义和创建绑定对象BindingManagerBase Dim 对象变量名 As BindingManagerBase 创建一个绑定对象BindingManagerBase需要指定其数据源,首先需要先填充数据集,然后可以用绑定对象的属性BindingContext来提供要绑定的数据源。 举例:声明一个名称为bd的BindingManagerBase类,并创建一个实例,设这里绑定的数据源是SqlDataAdapter1适配器生成的数据集DataSet11中的“T_Major”表。 Public bd As BindingManagerBase '定义绑定对象变量bd SqlDataAdapter1.Fill(DataSet11.T_Major) '填充数据集 bd = BindingContext(DataSet11, "T_Major") '指定绑定数据源
2. 绑定对象BindingManagerBase的属性 Position:获取或设置绑定到该数据源的控件所指向的数据表中的记录位置,如果数据集中的记录数为n,Position取值从0~n-1。通过调整Position的值可以达到浏览数据记录的目的。 Count:获取绑定对象的数据集的总记录数。 3. 绑定对象BindingManagerBase的方法 AddNew(),用于添加一空行; RemoveAt(BindingManagerBase.Position),用于删除指定行; CancelCurrentEdit(),撤销修改; EndCurrentEdit(),在窗体上直接修改,然后用该方法结束当前修改。 可以使用这些方法实现对数据源的更新操作。
14.2.2基于绑定对象的数据查询 数据绑定方法的主要思想是通过绑定对象的数据指示器(Position属性)同步显示或浏览数据库中的表数据。下面以专业信息显示为例来介绍数据绑定的实现。 基于绑定对象的数据查询的主要步骤是: 1)定义要访问的数据源,即使用数据适配器配置向导或程序代码生成数据集DataSet。 2)定义一个绑定对象BindingManagerBase,并在数据集填充时指定绑定对象的数据源。 3)根据应用需求,设计界面,将用于显示数据的控件分别绑定到需要的数据源DataSet 。 4)编程通过对BindingManagerBase操作实现所有窗体界面上数据控件的显示,从而完成数据导航。
【例14-4】基于数据绑定通过文本框实现学校开设的专业记录的浏览,见图。 实现过程: 1)窗体设计。 2)将服务器资源管理器中所需的数据表T_Major拖至窗体,生成数据集“T_Major”。 3)窗体控件的数据源绑定。设置每个文本框的绑定数据源,将文本框的DataBinding中的Text属性为数据集中T_Major表的相应字段。 4) 绑定对象的数据源绑定,通过代码实现绑定对象的数据源设置。 mybind = BindingContext(DataSet11,"T_Major") 5)编写程序实现数据浏览。用绑定对象mybind的属性Position来设置数据源中被访问表的行的索引号(也就是行号),即可达到数据记录浏览的目的。具体操作如下: 首条记录:mybind.Position = 0 往前翻:mybind.Position = mybind.Position – 1 往后翻:mybind.Position = mybind.Position + 1 最后一条记录:mybind.Position = mybind.Count()-1
在窗体类定义的开始处,申明绑定对象,这样可以让后面所有的Sub都可以访问它。 Imports System.Data.SqlClient ‘指明Sql对象的命名空间 Public Class Form1 Inherits System.Windows.Forms.Form Public mybind As BindingManagerBase '在此定义数据绑定对象mybind ‘启动窗体时绑定数据源 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.SqlDataAdapter1.Fill(Me.DataSet11.T_Major) mybind = Me.BindingContext(Me.DataSet11, "T_Major") '指定绑定对象的数据源 Me.DataGrid1.DataSource = Me.DataSet11.T_Major '指定控件的数据源 End Sub '按钮“|<”的代码,绑定对象通过属性Position来指示元组的位置或表中行的位置 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click mybind.Position = 0 '按钮“<<”的代码 Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click mybind.Position = mybind.Position - 1 '按钮“>>”的代码 Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click mybind.Position = mybind.Position + 1 '按钮“>|”的代码 Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click mybind.Position = mybind.Count - 1 '通过调整DataGrid数据指示器的位置,使文本框同步显示相关记录信息 Private Sub DataGrid1_Click(……) Handles DataGrid1.Click mybind.Position = DataGrid1.CurrentRowIndex End Class 为绑定对象指明数据源DataSet中某张表,一般在填充数据集后指定。 为了使DataGrid能同步显示数据指示器的位置,可在每个按钮事件过程代码最后增加如下语句: DataGrid1.CurrentRowIndex = mybind.Position
14.2.3基于绑定对象的数据更新 通过对绑定数据源DataSet执行操作Modify(修改)、AddNew(添加)和Remove(删除)来实现的,然后再用数据适配器的更新方法Update把数据集中所作的所有修改回写到物理数据库中去,从而完成对数据库的更新操作。 【例14-5】通过绑定对象实现对专业记录的增加、删除和修改操作,程序窗体界面如图14-20所示。
绑定对象的更新操作有下列方法调用: mybind.AddNew(),用于添加一空行; mybind.RemoveAt(mybind.Position),用于删除指定行; mybind.CancelCurrentEdit(),撤销修改; mybind.EndCurrentEdit(),在窗体上直接修改,然后用该方法结束当前修改。 用数据适配器的Update方法更新到数据源中,本例是对表“T_Major”的操作,实现代码为: SqlDataAdapter1.Update(Me.DataSet11. T_Major)
Imports System.Data.SqlClient Public Class Form1 Inherits System.Windows.Forms.Form Public mybind As BindingManagerBase ‘在此定义数据绑定对象mybind ‘……Windows窗口设计器生成的代码 ‘启动窗体时绑定数据源 Private Sub Form1_Load(……) Handles MyBase.Load SqlDataAdapter1.Fill(DataSet11.T_Major) ‘指定绑定对象的数据源 mybind = Me.BindingContext(Me.DataSet11, "T_Major") ‘指定控件的数据源 Me.DataGrid1.DataSource = Me.DataSet11.T_Major End Sub ‘添加一条记录 Private Sub Button5_Click(……) Handles Button5.Click mybind.AddNew() End Sub ‘删除一条记录 Private Sub Button6_Click(……) Handles Button6.Click mybind.RemoveAt(mybind.Position) ‘撤销更新 Private Sub Button7_Click(……) Handles Button9.Click mybind.CancelCurrentEdit() ‘确认更新,将修改通过Me.SqlDataAdapter1回存到数据源 Private Sub Button8_Click(……) Handles Button14.Click mybind.EndCurrentEdit() Me.SqlDataAdapter1.Update(Me.DataSet11.T_Major) ‘退出系统 Private Sub Button9_Click(……) Handles Button8.Click End ‘文本框的信息会随着DataGrid的变化而变化 Private Sub DataGrid1_Click(……) Handles DataGrid1.Click mybind.Position = Me.DataGrid1.CurrentRowIndex
14.3基于SqlCommand的数据访问 建立与数据库的连接后,可以通过SqlCommand对象执行SQL命令,直接访问数据源,在应用程序中调用存储过程也是通过SqlCommand对象实现的。 SqlCommand对象的SQL语句可以通过CommandText属性进行设置和修改。在创建SqlCommand对象时,需要设定两个参数: 在数据源上执行的SQL语句 数据库连接对象SqlConnection对象。 SqlCommand对象的执行方法: 应用方法 执行结果 适用场合 Dim myadapter As New SqlDataAdapter(SqlCommand) SqlCommand.Connection = SqlConnection SqlCommand..CommandText = T-SQL语句 为SqlDataAdapter提供命令,以便生成一个查询数据集,与直接使用SqlDataAdapter生成数据集结果相同 可反复读写的多行结果集合的查询。 SqlCommand.ExecuteReader 返回结果为一个只向前读的数据流,即SqlDataReader对象,存放着一个当前的结果行 只需要前向读的结果集合的查询或只含一个返回行的查询。 SqlCommand.ExecuteScalar 执行查询,并返回查询的结果集中第一行的第一列,即只返回一个单值。 对一个字段值(尤其是一个汇总结果)的查询。 SqlCommand.ExecuteNonQuery 更新数据库中的数据 更新(插入、修改、删除)的数据库操作
14.3.1 SqlCommand对象和SqlDataReader对象 SqlCommand对象 1)SqlCommand对象的定义 Dim 对象变量名 As New SqlCommand(命令字符串, SqlConnection对象变量) SqlConnection对象变量是已经创建的SqlConnection实例,该参数也可以直接用连接字符串替代,这样可以省去创建SqlConnection对象的步骤; 命令字符串是T_SQL的Select语句或存储过程。 例如:已创建了一个连接到School数据库的SqlConnection实例cn,声明一个名称为cmd的SqlCommand对象实例方法: Public cmd As SqlCommand ("Select * From T_Grade", cn)
2) SqlCommand对象的属性 CommandText:获取或设置要对数据源执行的T-SQL语句或存储过程。 CommandType:获取或设置CommandText的命令类型。Text表示是T-SQL语句,StoredProcedure表示是存储过程,TableDirect表示是表名。默认值是Text。 Connection:获取或设置 SqlCommand 的实例使用的SqlConnection。 因此,声明一个名称为cmd的SqlCommand对象实例也可采用如下方法: Dim cmd As New SqlCommand() '定义SqlCommand对象变量cmd cmd.CommandType = CommandType.Text '定义cmd的命令类型为T-SQL语句 cmd.CommandText = "Select * From T_Grade" '定义cmd的命令 cmd.Connection = cn '定义cmd所使用的数据库连接为cn 3)SqlCommand对象的方法 ExecuteNonQuery:执行T-SQL更新语句并返回受影响的行数。 ExecuteReader:执行T-SQL查询语句并生成一个前向只读数据流SqlDataReader,仅存放一个结果行,可读取下一条记录获得新的结果行。 ExecuteScalar:执行T-SQL查询语句,并返回查询结果集中第一行的第一列。
2.SqlDataReader对象 SqlDataReader提供一种从数据库快速的只向前读的数据流方式,存放SqlCommand对象的ExecuteReader方法的返回结果。 可以通过循环程序一行一行地获取多条数据,但该对象当前只存储一行,而且只要一行被读取,该行之前的数据就不再有效,为了再次读取那一行,必须创建一个新的SqlDataReader实例并且再次从数据流中读取它。不能使用它来写入数据。 不但节省资源而且效率很高,故可以降低网络的负载。 1)SqlDataReader对象的定义 Dim 对象变量名 As New SqlDataReader 例如: 声明并创建一个名称为myread的对象实例: Dim myread As SqlDataReader 2)SqlDataReader对象的方法 Read():读取到下一条记录。 Close():关闭SqlDataReader对象。 注意:一个数据库连接只能被一个SqlDataReader使用,在SqlDataReader没有关闭之前,数据库连接会一直被占用,因此使用完毕应该马上关闭它。
14.3.2基于SqlCommand的数据查询 1.利用SqlCommand产生数据集和SqlDataReader对象 【例14-6】完成课程考核成绩信息查询。具体要求为:给定课程号,查询该课程的课程名、最高分数、最低分数和平均分数。程序界面见图14-21,其中课程号通过组合框ComboBox选择或输入。
1)利用SqlCommand产生数据集 数据查询的数据输入是来自控件ComboBox1的课程号集。以SqlCommand中的查询命令实现控件ComboBox1中的课程号填充。 Private Sub Form1_Load(…) Handles MyBase.Load Dim myconn As New SqlConnection("Initial Catalog=school;Data Source=(Local); " _ & "Integrated Security=SSPI;") '创建SqlConnection对象myconn Dim sql As String = "Select CourseCode From T_Course" '定义查询语句赋值给sql变量 Dim mycmd As New SqlCommand() '创建SqlCommand对象mycmd mycmd.Connection = myconn '定义SqlCommand对象数据库连接 mycmd.CommandText = sql '定义SqlCommand对象数据查询命令 Dim myadapter As New SqlDataAdapter(mycmd) '定义SqlDataAdapter对象myadapter Dim mydataset As New DataSet() '定义DataSet对象mydataset myadapter.Fill(mydataset, "课程号") '填充mydataset"课程号"表 ComboBox1.DataSource = mydataset.Tables("课程号") '指定ComboBox1数据源 '获取"课程号"表中的第0列数据, 用于ComboBox1显示,其中ToString是Columns(0)对象 '的方法,功能是获取该列的值 ComboBox1.DisplayMember = mydataset.Tables("课程号").Columns(0).ToString End Sub 比较:在例14-2中,下列语句完成和本例中粗体语句相同的功能 Dim myadapter As SqlDataAdapter = New SqlDataAdapter(sql, myconn)
2)利用SqlCommand产生SqlDataReader对象 从组合框ComboBox1中选择一个课程号时,SelectedIndexChanged事件发生 Private Sub ComboBox1_SelectedIndexChanged(…) Handles ComboBox1.SelectedIndexChanged Dim sql As String = "select coursename,max(grade),min(grade),avg(grade) from T_grade, " _ & "T_course where T_grade.coursecode = T_course.coursecode and T_grade.coursecode='" & _ ComboBox1.Text.Trim & "'group by t_grade.coursecode,coursename " Dim myconn As New SqlConnection("Initial Catalog=school; Data Source=(Local); Integrated Security=SSPI;") Dim mycmd As SqlCommand = New SqlCommand(sql, myconn) Dim myread As SqlDataReader '定义SqlDataReader对象myread myconn.Open() myread = mycmd.ExecuteReader '产生myread对象空间 myread.Read '通过读操作Read()读取一行数据 TextBox1.Text = myread.Item(0) '获得查询结果的课程名 TextBox2.Text = myread.Item(1) '获得查询结果的最高分数 TextBox3.Text = myread.Item(2) '获得查询结果的最低分数 TextBox4.Text = Val(myread.Item(3)).ToString("###.#") '获得查询结果的平均分数 myread.close() myconn.close() End Sub '退出按钮的程序 Private Sub Button1_Click(…) Handles Button1.Click End '退出系统 Dim mycmd As New SqlCommand mycmd.CommandText = sql mycmd.Connection = myconn 备注:本例查询结果只有一行,如果有多条记录需要读取,可以使用循环语句,例如: While(myread.Read()) ...... End While
2.利用SqlCommand的ExecuteScalar执行方法返回单个值 只返回查询结果中的第一行第一列,即只返回单个值。 【例14-7】完成课程信息统计。具体要求是,给定课程号查询该课程的课程名称、参加考试人数和不及格人数。 (1)生成课程号数据集,填充组合框 设计查询,将课程表的课程号填充到数据集,并指定 为控件ComboBox1的数据源。 '在窗体开始处定义SqlConnection对象, 便于窗体中多个事件过程共享 Dim myconn As New SqlConnection("Initial Catalog=School;Data Source=(Local); " _ & "Integrated Security=SSPI;") 'Form1_Load()事件过程利用数据集获得课程号数据,并由ComboBox1显示 Private Sub Form1_Load(…) Handles MyBase.Load Dim sql As String = "Select CourseCode From T_Course " '定义查询语句赋值给sql变量 Dim myadapter As New SqlDataAdapter(sql, myconn) '定义SqlDataAdapter对象myadapter Dim mydataset As New DataSet() '定义DataSet对象mydataset myadapter.Fill(mydataset, "课程号") '填充mydataset"课程号"表 ComboBox1.DataSource = mydataset.Tables("课程号") '指定ComboBox1数据源 ‘获取"课程号"表中的第0列数据, 其中ToString是Columns(0)对象的方法, 获取该列的值 ComboBox1.DisplayMember = mydataset.Tables("课程号").Columns(0).ToString End Sub
(2)利用DataReader产生查询结果中的课程名和考核人数 '从ComboBox1选择课程号,查询课程名称、考核人数和不及格人数 Private Sub ComboBox1_SelectedIndexChanged(…) Handles ComboBox1.SelectedIndexChanged '以下代码使用SqlCommand的ExecuteReader方法查询课程名和考核人数 myconn.Open() '打开数据库连接myconn Dim sql As String = "Select CourseName, Count(StudentCode) From T_Course Join T_Grade " _ & " On T_Grade.CourseCode = T_Course.CourseCode Where T_Grade.CourseCode='" _ & ComboBox1.Text & "'Group By CourseName " Dim mycmd As New SqlCommand(sql, myconn) '定义SqlCommand对象mycmd Dim myread As SqlDataReader '定义SqlDataReader对象myread myread = mycmd.ExecuteReader '执行mycmd.ExecuteReader产生myread对象空间 While myread.Read() '读取myread中的下一条记录 TextBox1.Text = myread.Item(0) '获取课程名称 TextBox2.Text = myread.Item(1) '获取考核人数 End While myread.Close() '关闭myread
(3)用SqlCommand的ExecuteScalar方法查询不及格人数 '以下代码使用SqlCommand的ExecuteScalar方法查询不及格人数,并赋值给TextBox3 sql = "Select Count(StudentCode) From T_Grade Where Grade<60 and " _ & "CourseCode='" & ComboBox1.Text & "'" '给sql变量重新赋值 mycmd.CommandText = sql '修改mycmd的CommandText为新的SQL命令 TextBox3.Text = mycmd.ExecuteScalar ' mycmd.ExecuteScalar获得不及格人数 myconn.Close() '关闭数据库连接myconn End Sub '退出系统 Private Sub Button1_Click(…) Handles Button1.Click End End Sub myconn.Open()
14.3.3基于SqlCommand的数据更新 Sqlcommand的ExecuteNonQuery方法执行SQL更新命令并返回受影响的记录。即通过执行 UPDATE、INSERT 或 DELETE 语句,在不使用 DataSet 的情况下更改数据库中的数据,返回值为该命令所影响的行数。 【例14-8】完成选课表的数据插入操作。具体要求是从窗体文本框内输入相关选课信息,通过【确定】按钮完成数据表T_Grade的数据插入操作。功能界面参见图。 一般前台的数据更新操作多用SqlCommand的ExecuteNonQuery方法。该方法的执行不会给前台带来任何数据返回,只是将后台的操作结果所影响的行数作为命令执行结果返回给前台。
Private Sub Button1_Click(……) Handles Button1.Click ‘定义SQL更新命令 Dim sql As String = "Insert Into T_grade values( '" & TextBox1.Text.ToString() & "','"_ & TextBox2.Text.ToString() & "'," & Val(TextBox3.Text.Trim) & ")" Dim myconn As New SqlConnection("Initial Catalog=school; Data Source=(Local); "_ & "Integrated Security=SSPI;") '定义和创建数据库连接myconn ‘创建SqlCommand对象mycmd Dim mycmd As SqlCommand = New SqlCommand(sql, myconn) Dim i As Integer myconn.Open() '执行命令,如果出错捕获和报告错误 Try i = mycmd.ExecuteNonQuery() '用i变量检查命令执行影响的数据行数 Catch ex As Exception '捕获Try后的程序段执行异常 MsgBox(ex.Message) '报告出错的异常原因 End Try myconn.Close() '关闭数据库连接myconn MsgBox(i & "条记录成功添加") '报告添加结果 End Sub ‘撤销输入 Private Sub Button2_Click(…) Handles Button2.Click TextBox1.Text = "" TextBox2.Text = "" TextBox3.Text = ""
14.3.3调用存储过程 存储过程是存储在SQL Server端的数据库中一个已经编译好的可执行SQL语句段,由于它的执行效率高于前台发出的SQL命令请求,所以广泛地应用于各种B/S模式的数据库应用系统。 1.无参数存储过程的调用 【例14-9】调用存储过程,完成课程表的数据浏览。 (1)创建存储过程 在SQL Server上创建一个存储过程proc_course, 返回的是学校开设的所有课程的信息。 CREATE proc proc_Course AS SELECT * from T_Course 这是一个无输入、输出参数的存储过程,并且执行结果返回一张表的数据。 (2)调用存储过程 在VB.NET程序中调用存储过程的方法类似于SqlCommand对象的执行。 1)创建一个SqlCommand对象。 2)设置CommandType属性,把它配置为访问存储过程。 3)添加与存储过程本身匹配的参数,对于无参数的存储过程可以省略。
Private Sub Button1_Click(…) Handles Button1.Click Dim myconn As New SqlConnection("Initial Catalog=school; Data Source=(Local);" _ & "Integrated Security=SSPI;") '创建数据库连接对象myconn '创建SqlCommand对象mycmd,其命令串为存储过程的名称"proc_course" Dim mycmd As SqlCommand mycmd = New SqlCommand("proc_course", myconn) mycmd.CommandType = CommandType.StoredProcedure '声明命令的类型为存储过程 Dim myadapter As New SqlDataAdapter(mycmd) '创建数据适配器对象myadapter Dim mydataset As New DataSet() '创建数据集对象mydataset myadapter.Fill(mydataset, "T_Course") '填充数据集 DataGrid1.DataSource = mydataset.Tables("T_Course") '为DataGrid1控件指定数据源 End Sub
【例14-10】调用带有输入参数的存储过程,完成给定学号的学生平均成绩的查询操作。 2.有参数的存储过程的调用 【例14-10】调用带有输入参数的存储过程,完成给定学号的学生平均成绩的查询操作。 (1)创建存储过程 CREATE PROC proc_getavg @_snum CHAR(8) AS SELECT AVG(grade) FROM T_grade WHERE studentcode=@_snum 存储过程proc_getavg的功能是返回给定学号的学生平均成绩。输入参数是学生学号。 (2)调用存储过程 本例在上例的基础上增加了存储过程参数的定义。参数的定义分两步进行: 1)定义参数及其类型 基于本例的参数myParm的定义如下: Dim myParm As SqlParameter = mycmd.Parameters.Add("@_snum",SqlDbType.Char, 8) 2)为输入参数赋值 myParm.Value = TextBox1.Text 说明:如果是输出参数,不给参数赋值,但要指明它是输出参数,即用下列方式指明输出参数的direction属性。 myParm.Direction = ParameterDirection.Output
Private Sub Button1_Click(…) Handles Button1.Click Dim myconn As New SqlConnection("Initial Catalog=School;Data Source=(Local); " _ & "Integrated Security=SSPI;") '定义数据库连接SqlConnection '创建SqlCommand对象mycmd,其命令串为存储过程"proc_getavg" Dim mycmd As New SqlCommand("proc_getavg", myconn) mycmd.CommandType = CommandType.StoredProcedure '声明命令的类型为存储过程 '为mycmd添加输入参数myParm,对应于存储过程的变量@_snum Dim myParm As SqlParameter = mycmd.Parameters.Add("@_snum", SqlDbType.Char, 8) myParm.Value = TextBox1.Text '为输入参数赋值 myconn.Open() '打开数据连接 TextBox2.Text = Format(mycmd.ExecuteScalar, "0.0") '执行查询mycmd.ExecuteScalar myconn.Close() '关闭数据连接 End Sub 说明:Format(mycmd.ExecuteScalar,"0.0")是数据格式的控制函数,是将mycmd. ExecuteScalar的执行结果值保留一位小数。
14.4ADO.NET的数据访问实例 ADO.NET对象模型为用户提供了丰富的数据库访问方法,归纳为三种: (1)通过SqlCommand直接访问数据库进行查询或更新; (2)通过数据绑定方法对数据库进行查询和更新操作; (3)通过DataAdapter和DataSet对象进行数据库的数据查询或数据更新操作。 这三种方法中最有特色的是第三种,它也是ADO.NET数据库访问技术的核心,因为它提供了一种断开式数据访问的新模型,提高了远程数据访问性能。
【例14-11】完成学生成绩查询功能。具体要求为:给定学号,查询学生的选课记录(姓名,课程名称,成绩)和平均成绩。运行结果界面见图,窗体控件属性设置见表。 Label1 Text 学生成绩查询系统 Label2 请输入学号: Label3 平均成绩 Label4 Name avg GroupBox1 成绩单 TextBox1 snum Button1 查询 Form1 查分系统
(1)窗体设计 1)文本框:输入待查成绩的学生学号; 2)标签:输出学生的平均成绩; 3)数据网格:输出学生的选课记录。 (2)代码设计 这是一个多表查询。对于多表查询,首先要清楚各个表之间的关系,然后才能写出正确的数据查询命令。各个表之间的数据关联见关系图14-27。
Private Sub Button1_Click(…) Handles Button1.Click Dim myconn As New SqlConnection("Initial Catalog=School; Data Source=(Local);" _ & "Integrated Security=SSPI") '创建SqlConnection对象myconn '定义Sql语句,查询指定学号的学生选课记录,包括属性:学生姓名、课程名称和成绩 Dim sql As String = "Select StudentName, CourseName, Grade from T_Grade, " _ & "T_Student, T_Course Where T_Grade.StudentCode=T_Student.StudentCode and " _ & "T_Grade.CourseCode=T_Course.CourseCode and T_Grade.StudentCode='" _ & snum.Text & "'" Dim myadapter As New SqlDataAdapter(sql, myconn) '定义SqlDataAdapter对myadapter Dim mydataset As New DataSet() '定义DataSet对象mydataset myadapter.Fill(mydataset, "成绩单") '填充mydataset"成绩单" DataGrid1.DataSource = mydataset.Tables("成绩单") '指定ComboBox1数据源 Dim i, sum As Integer 'sum用于累加成绩 For i = 0 To mydataset.Tables("成绩单").Rows.Count - 1 '将选修的每门课程的成绩取出来作累加 sum = sum + mydataset.Tables("成绩单").Rows(i).Item(2) Next avg.Text = sum / i ‘计算并显示平均成绩 End Sub