VB.NET学习笔记:ADO.NET操作ACCESS数据库——数据集DataSet数据管理(DataAdapter查询更新数据库)

DataAdapter与数据源提供程序相关,操作ACCESS数据库数据提供程序为OleDb.net,命名空间为System.Data.OleDb,相应的DataAdapter对象在System.Data.OleDb空间里对应的是OleDbDataAdapter对象。
而DataTable对象和DataSet对象同属于System.Data命名空间,可见其与数据源无关。DataTable对象可作为DataSet对象中的表,也可以单独作为数据集。下图为ADO.NET 体系结构。
ADO.NET 体系结构
使用OleDbDataAdapter对象的Fill方法可从数据源检索数据并填充 DataSet 中的表,实例化OleDbDataAdapter对象时必须设置其SelectCommand属性。 使用OleDbDataAdapter对象的Update方法还可将对 DataSet 所做的更改解析回数据源,实例化OleDbDataAdapter对象时必须设置InsertCommand、 UpdateCommand或 DeleteCommand属性。

一、OleDbDataAdapter对象填充数据集并将更改保存到数据库

以下示例代码演示使用OleDbDataAdapter对象填充数据集,修改数据集中的表数据,然后将修改过的数据更新到数据库。

Imports System.Data.OleDb
Public Class Form2

    Private Const strconn As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=F:\test.accdb"
    Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        '连接数据库
        Dim conn As New OleDbConnection(strconn)
        '打开数据库
        conn.Open()
        '实例化OleDbDataAdapter
        Dim adapter As New OleDbDataAdapter("SELECT stutbl.* FROM stutbl", conn)
        '实例化DataSet
        Dim dst As New DataSet("mydst")
        '添加表
        dst.Tables.Add("mytbl")
        '填充数据到DataSet
        adapter.Fill(dst, "mytbl")
        '窗口显示数据
        DataGridView1.DataSource = dst.Tables("mytbl")

        '操作数据表DataTable
        With dst.Tables("mytbl")
            '插入行
            Dim newrow As DataRow = .NewRow()
            newrow("stuname") = "李四"
            newrow("sex") = "男"
            newrow(3) = "二年级"
            .Rows.Add(newrow)

            '更新行
            .Rows(2)("stuname") = "王二"

            '删除行
            .Rows(.Rows.Count - 2).Delete()
        End With

        '添加insert命令参数
        Dim insertcmd As New OleDbCommand("Insert INTO stutbl(sex,stuname,grade) VALUES (?,?,?)", conn)
        insertcmd.Parameters.Add("@sex", OleDbType.VarChar, 1, "sex")
        insertcmd.Parameters.Add("@stuname", OleDbType.VarChar, 10, "stuname")
        insertcmd.Parameters.Add("@grade", OleDbType.VarChar, 10, "grade")
        adapter.InsertCommand = insertcmd

        '添加update命令参数
        Dim updatecmd As New OleDbCommand("Update stutbl set sex=?,stuname=?,grade=? Where ID=?", conn)
        updatecmd.Parameters.Add("@sex", OleDbType.VarChar, 1, "sex")
        updatecmd.Parameters.Add("@stuname", OleDbType.VarChar, 10, "stuname")
        updatecmd.Parameters.Add("@grade", OleDbType.VarChar, 10, "grade")
        updatecmd.Parameters.Add("@ID", OleDbType.VarChar, 10, "ID").SourceVersion = DataRowVersion.Original
        adapter.UpdateCommand = updatecmd

        '添加delete命令参数
        Dim deletecmd As New OleDbCommand("Delete * FROM stutbl Where ID=?", conn)
        deletecmd.Parameters.Add("@ID", OleDbType.BigInt, Nothing, "ID").SourceVersion = DataRowVersion.Original
        adapter.DeleteCommand = deletecmd

        Dim dstchanges As DataSet
        '检查数据是否发生更改
        If dst.HasChanges Then
            '将更改的行保存到新数据集中
            dstchanges = dst.GetChanges()
            '将更改显示到窗口
            DataGridView2.DataSource = dstchanges.Tables("mytbl")

            '检查发生更改的行是否有错误
            If dstchanges.HasErrors() Then
                '拒绝更改
                dst.RejectChanges()
            Else
                '更新数据库
                adapter.Update(dstchanges, "mytbl")
            End If
        End If

        '关闭数据库
        conn.Close()
    End Sub
End Class

代码解读:
1、操作数据表部分代码,如添加新行、更新行数据、删除行等可以不用,直接在DataGridView控件修改数据即可即时反映回数据集。写这部分代码主要是为了做笔记。
需要特别注意的是删除行的方法有两个:Delete方法和Remove方法。
Delete方法只是把该行的行状态修改为Deleted,此行还在数据集中,并没有真正的删除。可以使用DataAdapter的update方法来删除数据库中对应的这一行。
Remove方法是真的把该行从数据集移除了,此行真的不在数据集中,使用DataAdapter的update方法时不会删除数据库中对应的这一行的。
2、为命令添加参数的语句,如

updatecmd.Parameters.Add("@sex", OleDbType.VarChar, 1, "sex")

第一个参数"@sex"是Parameter的名称,可以自由发挥,但为了更好解读代码,最好对应数据集中的列名,最后一个参数"sex"是指这个参数的值从数据集中的"mytbl"表的sex列取值,这个不是数据库中的列,需要特别注意。
3、添加update命令和delete命令参数时需要设置主键列的SourceVersion属性值为DataRowVersion.Original,这是表示取从数据库加载时的值,也就是添加到数据集中最原始的值。因为可能在数据集中修改了该主键的值,这样的话就没办法在数据库找到该主键值或找错,造成更新出错。相关内容可以参阅官网帮助《行状态和行版本

二、OleDbCommandBuilder 对象自动生成更新命令的SQL语句

看着为命令添加参数的代码就头痛,好哆嗦,其实也有简洁的方法。
可以把这一长串代码:

'添加insert命令参数
        Dim insertcmd As New OleDbCommand("Insert INTO stutbl(sex,stuname,grade) VALUES (?,?,?)", conn)
        insertcmd.Parameters.Add("@sex", OleDbType.VarChar, 1, "sex")
        insertcmd.Parameters.Add("@stuname", OleDbType.VarChar, 10, "stuname")
        insertcmd.Parameters.Add("@grade", OleDbType.VarChar, 10, "grade")
        adapter.InsertCommand = insertcmd

        '添加update命令参数
        Dim updatecmd As New OleDbCommand("Update stutbl set sex=?,stuname=?,grade=? Where ID=?", conn)
        updatecmd.Parameters.Add("@sex", OleDbType.VarChar, 1, "sex")
        updatecmd.Parameters.Add("@stuname", OleDbType.VarChar, 10, "stuname")
        updatecmd.Parameters.Add("@grade", OleDbType.VarChar, 10, "grade")
        updatecmd.Parameters.Add("@ID", OleDbType.VarChar, 10, "ID").SourceVersion = DataRowVersion.Original
        adapter.UpdateCommand = updatecmd

        '添加delete命令参数
        Dim deletecmd As New OleDbCommand("Delete * FROM stutbl Where ID=?", conn)
        deletecmd.Parameters.Add("@ID", OleDbType.BigInt, Nothing, "ID").SourceVersion = DataRowVersion.Original
        adapter.DeleteCommand = deletecmd

用如下代码替换:

Dim builder As OleDbCommandBuilder = New OleDbCommandBuilder(adapter)
        builder.GetUpdateCommand()
        builder.GetDeleteCommand()
        builder.GetInsertCommand()

OleDbCommandBuilder 对象可以自动生成OleDbDataAdapter对象的DeleteCommand 、 InsertCommand和UpdateCommand 命令 。
但使用OleDbCommandBuilder时需注意:
1、数据集必须是单个数据库表中生成,也就是说只能更新单个表。
2、为了能够自动生成命令,必须设置 SelectCommand 属性,这是最低要求。SelectCommand 还必须至少返回一个主键或唯一列。 如果不存在任何主键和唯一列,则会生成 InvalidOperation 异常,并且不会生成命令。
3、如果您在自动生成 INSERT、UPDATE 或 DELETE 命令后修改 SelectCommand,则可能会发生异常。为避免出错,可以通过调用 OleDbCommandBuilder 的 RefreshSchema 方法来刷新由OleDbCommandBuilder。

三、将数据库表架构填充到数据集中

连续使用OleDbDataAdapter对象Fill方法填充数据集,因为没有主键(唯一)约束,刷新数据时可能会出现数据重复,如:
重复数据
可以先使用OleDbDataAdapter对象FillSchema方法自动将数据库中的表架构填充到数据集中,然后再用Fill方法将数据填充到数据集。

adapter.FillSchema(dst.Tables("mytbl"), SchemaType.Mapped)
adapter.Fill(dst, "mytbl")

有了主键约束就不会出现重复数据了,但这只能用于单表查询结果集,如果是多表查询结果集只能自定义数据集中的表架构了。
其实也有简单方法:先使用DataTable的Clear方法清除表内的所有数据,然后再调用OleDbDataAdapter对象Fill方法重新填充(即刷新数据)DataTable,这样就不会有重复数据出现了,如:

dst.Tables("mytbl").Clear()
adapter.Fill(dst, "stutbl")

使用DataSet对象的Clear方法可以清除DataSet数据集中所有表内的所有数据行。

四、自定义表构架并将数据库数据填充到数据集

Public Class Form2
    Private Const strconn As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=F:\test.accdb"
    Private dst As DataSet
    Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        dst = New DataSet()
        '添加表
        Dim dt As DataTable = dst.Tables.Add("mytbl")

        '添加列
        dt.Columns.Add("stsex", Type.GetType("System.String"))
        dt.Columns.Add("stgrade", Type.GetType("System.String"))
        dt.Columns.Add("stname", Type.GetType("System.String"))

        '添加自增长和主键
        Dim coldi As DataColumn = dt.Columns.Add("di", Type.GetType("System.Int32"))
        coldi.AutoIncrement = True '设置为自增长列
        '添加主键约束
        Dim columns(0) As DataColumn
        columns(0) = coldi
        dt.PrimaryKey = columns

        '自定义DataGridView列
        DataGridView1.AutoGenerateColumns = False '不自动创建列
        DataGridView1.DataSource = dt '数据源

        Dim colID As New DataGridViewTextBoxColumn()
        colID.DataPropertyName = "di"
        colID.HeaderText = "编号"
        colID.Name = "ID"
        DataGridView1.Columns.Add(colID)

        Dim colname As New DataGridViewTextBoxColumn()
        With colname
            .DataPropertyName = "stname"
            .HeaderText = "姓名"
            .Name = "stuname"
        End With
        DataGridView1.Columns.Add(colname)

        Dim colsex As New DataGridViewTextBoxColumn()
        With colsex
            .DataPropertyName = "stsex"
            .HeaderText = "性别"
            .Name = "stusex"
        End With
        DataGridView1.Columns.Add(colsex)

        Dim colgrade As New DataGridViewTextBoxColumn()
        With colgrade
            .DataPropertyName = "stgrade"
            .HeaderText = "年级"
            .Name = "stugrade"
        End With
        DataGridView1.Columns.Add(colgrade)
    End Sub

    '查询数据库填充数据集并显示到窗体
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        '连接数据库
        Dim conn As New OleDbConnection(strconn)
        '打开数据库
        conn.Open()
        
        '实例化OleDbDataAdapter
        Dim strSQL As String = "SELECT studenttbl.ID as di,studenttbl.stuname as stname, sextbl.sex as stsex, gradetbl.grade as stgrade" _
        & " FROM sextbl,studenttbl,gradetbl" _
        & " WHERE (([studenttbl].[gradeID]=[gradetbl].[gradeID]) AND ([studenttbl].[sexID]=[sextbl].[sexID]));"
        Dim adapter As New OleDbDataAdapter(strSQL, conn)
        '填充数据到DataSet
        dst.Tables("mytbl").Columns("di").AutoIncrement = False
        adapter.Fill(dst, "mytbl")
        dst.Tables("mytbl").Columns("di").AutoIncrement = True
        
       '关闭数据库
        conn.Close()
    End Sub
End Class

将数据集声明为公有变量并在加载窗体时自定义表架构,特别注意的是在设置SelectCommand命令的SQL语句时,查询结果集各列名称要与数据集定义的列名称相同。

五、使用ColumnMappings关联OleDbDataAdapter与DataTable间的列

如果查询结果集各列名称与数据集定义的列名称不相同的话,还可以这样处理:使用OleDbDataAdapter的TableMappings属性来关联DataSet中DataTable名称与OleDbDataAdapter源表名称,然后再使用DataTableMapping的ColumnMappings属性来关联DataTable与OleDbDataAdapter各列名称。
把按钮Button1的单击事件代码修改如下:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        '连接数据库
        Dim conn As New OleDbConnection(strconn)
        '打开数据库
        conn.Open()
        
        '实例化OleDbDataAdapter
        Dim strSQL As String = "SELECT studenttbl.ID,studenttbl.stuname, sextbl.sex, gradetbl.grade" _
        & " FROM sextbl,studenttbl,gradetbl" _
        & " WHERE (([studenttbl].[gradeID]=[gradetbl].[gradeID]) AND ([studenttbl].[sexID]=[sextbl].[sexID]));"
        
        Dim adapter As New OleDbDataAdapter(strSQL, conn)

        Dim mapping As System.Data.Common.DataTableMapping = adapter.TableMappings.Add("stutbl", "mytbl") '关联表名
        '关联列名
        mapping.ColumnMappings.Add("ID", "di")
        mapping.ColumnMappings.Add("stuname", "stname")
        mapping.ColumnMappings.Add("sex", "stsex")
        mapping.ColumnMappings.Add("grade", "stgrade")
        
        '填充数据到DataSet
        adapter.Fill(dst, "stutbl")
        
        '关闭数据库
        conn.Close()
    End Sub

写在最后:

1、调用OleDbDataAdapter对象Fill方法时,不需要手工打开OleDbConnection。

如果OleDbDataAdapter对象的SelectCommand属性的OleDbConnection已经关闭,OleDbDataAdapter将自动打开连接、提交查询、提取结果,然后关闭OleDbConnection。但是如果在调用Fill方法之前已经打开OleDbConnection,调用以后OleDbConnection仍然处于打开状态。
如果用多个OleDbDataAdapter对象将多个查询的结果填充到DaraSet中,不手动打开OleDbConnection的话,每次都调用OleDbDataAdapter对象的Fill方法都会打开和关闭OleDbConnection,为了避免重复地多次打开和关闭OleDbConnection对象,在对OleDbDataAdapter对象调用Fill方法之前,先调用OleDbConnection对象的Open方法。填充数据结束后调用OleDbConnection对象的Close方法关闭OleDbConnection。如:

'打开数据库
conn.Open()
adapter.Fill(dst, "stutbl")
adapter1.Fill(dst, "stutbl")
'关闭数据库
conn.Close()

2、ACCESS每次只能执行一条SQL语句,所以当然不会支持逻辑语句,也就写不出真正意义的存储过程。

如果试图在SelectCommand等属性里设置多条SQL语句会报错,提示“System.Data.OleDb.OleDbException:“在 SQL 语句结尾之后找到字符。”。如图:
在这里插入图片描述
试图进行多表查询然后更新有一定的难度,不懂该如何写SQL语句。

猜你喜欢

转载自blog.csdn.net/zyjq52uys/article/details/88665663