本章では、VB.NETからデータベースを安全かつ効率的に扱うためのADO.NETの高度活用について学習します。
業務システムにおいて、データベース連携は最も重要な要素のひとつです。 顧客情報、受注情報、在庫情報、社員情報、請求情報など、ほとんどの業務データはデータベースに保存されます。 そのため、VB.NETアプリケーションからDBを正しく扱えることは、実務開発において必須のスキルです。
ADO.NETは、.NETにおける基本的なデータアクセス技術です。 Entity FrameworkなどのORMを使う場合でも、内部的なDB接続やトランザクション、SQL実行の仕組みを理解するうえで、 ADO.NETの知識は非常に重要です。
本章のゴールは、単にSELECTやINSERTを実行できるようになることではありません。 Connection、Command、DataReader、DataAdapter、DataSet、DataTable、Transaction、接続プーリングなどを理解し、 安全性、性能、保守性を考慮したDB連携を実装できるようになることです。
1. ADO.NETの構造
ADO.NETは、.NETアプリケーションからデータベースへアクセスするためのデータアクセス技術です。 VB.NETでは、SQL Server、Oracle、MySQL、PostgreSQLなど、さまざまなデータベースと連携できます。
ADO.NETでは、主に以下のようなクラスを利用してDB操作を行います。
- Connection:データベースへの接続を管理する
- Command:SQL文やストアドプロシージャを実行する
- DataReader:検索結果を高速に読み取る
- DataAdapter:DBとDataSet / DataTableの橋渡しを行う
- DataSet:メモリ上に複数のテーブルを保持する
- DataTable:メモリ上に1つの表形式データを保持する
- Transaction:複数のDB操作をひとつの処理単位として管理する
ADO.NETの基本的な流れ
1. Connectionを作成する
2. ConnectionをOpenする
3. Commandを作成する
4. SQL文またはストアドプロシージャを設定する
5. パラメータを設定する
6. ExecuteReader / ExecuteNonQuery / ExecuteScalarなどで実行する
7. 結果を取得する
8. ConnectionをClose / Disposeする
DB連携では、接続を開いたままにしないことが重要です。 接続は必要なタイミングで開き、処理が終わったら必ず閉じる必要があります。 VB.NETではUsingブロックを使うことで、例外が発生した場合でも確実にDisposeできます。
Usingを使った基本構造
Dim connectionString As String = "Data Source=.;Initial Catalog=SampleDb;Integrated Security=True"
Using connection As New SqlClient.SqlConnection(connectionString)
connection.Open()
Using command As New SqlClient.SqlCommand()
command.Connection = connection
command.CommandText = "SELECT COUNT(*) FROM Users"
Dim count As Integer = CInt(command.ExecuteScalar())
Console.WriteLine(count)
End Using
End Using
Usingを使うことで、ConnectionやCommandの破棄を明示的に管理できます。 DB接続は限りあるリソースであるため、必ず適切に解放する設計が必要です。
2. Connection / Command / DataReader / DataAdapter
ADO.NETの中核となるのが、Connection、Command、DataReader、DataAdapterです。 これらの役割を正しく理解することで、用途に応じた効率的なDBアクセスが可能になります。
Connection
Connectionは、アプリケーションとデータベースの接続を表します。 SQL Serverを利用する場合はSqlConnectionを使用します。
Using connection As New SqlClient.SqlConnection(connectionString)
connection.Open()
' DB処理
End Using
ConnectionはOpenしたら必ずCloseまたはDisposeする必要があります。 Usingブロックを使うことで、処理終了時に自動的にDisposeされます。
Command
Commandは、SQL文やストアドプロシージャを実行するためのオブジェクトです。 SELECT、INSERT、UPDATE、DELETEなどのSQLをCommandTextに設定して実行します。
Using command As New SqlClient.SqlCommand()
command.Connection = connection
command.CommandText = "SELECT UserName FROM Users WHERE UserId = @UserId"
command.Parameters.AddWithValue("@UserId", 1)
Dim userName As String = CStr(command.ExecuteScalar())
End Using
Commandの主な実行メソッド
| メソッド | 用途 | 戻り値 |
|---|---|---|
| ExecuteReader | 複数行の検索結果を読み取る | DataReader |
| ExecuteNonQuery | INSERT / UPDATE / DELETEを実行する | 影響を受けた行数 |
| ExecuteScalar | 1行1列の値を取得する | Object |
DataReader
DataReaderは、検索結果を前方に一行ずつ読み取るためのオブジェクトです。 高速でメモリ効率が良いため、大量データを順番に処理する場合に向いています。
Using command As New SqlClient.SqlCommand("SELECT UserId, UserName FROM Users", connection)
Using reader As SqlClient.SqlDataReader = command.ExecuteReader()
While reader.Read()
Dim userId As Integer = CInt(reader("UserId"))
Dim userName As String = CStr(reader("UserName"))
Console.WriteLine(userId & ":" & userName)
End While
End Using
End Using
DataReaderは高速ですが、接続を開いたまま読み取る必要があります。 そのため、読み取り中はConnectionが使用中になります。 必要なデータを読み取ったら、すぐにClose / Disposeすることが重要です。
DataAdapter
DataAdapterは、データベースとDataTable / DataSetの間を橋渡しするオブジェクトです。 検索結果をDataTableにまとめて読み込む場合によく使用されます。
Dim table As New DataTable()
Using connection As New SqlClient.SqlConnection(connectionString)
Using command As New SqlClient.SqlCommand("SELECT UserId, UserName FROM Users", connection)
Using adapter As New SqlClient.SqlDataAdapter(command)
adapter.Fill(table)
End Using
End Using
End Using
DataAdapterを使うと、検索結果をメモリ上のDataTableとして扱えます。 DataGridViewにバインドする場合などに便利です。
DataReaderとDataAdapterの使い分け
| 項目 | DataReader | DataAdapter |
|---|---|---|
| 読み取り方式 | 前方一方向に読み取る | DataTableにまとめて読み込む |
| 速度 | 高速 | DataReaderよりやや重い |
| メモリ使用量 | 少ない | データ量に応じて増える |
| 接続状態 | 読み取り中は接続が必要 | Fill後は接続を閉じられる |
| 向いている用途 | 大量データの逐次処理 | 画面表示、DataGridView連携 |
3. トランザクション制御
トランザクションとは、複数のDB操作をひとつの処理単位として扱う仕組みです。 すべて成功した場合のみ確定し、途中でエラーが発生した場合はすべて取り消します。
業務システムでは、複数テーブルへの登録や更新が必要になる場面が多くあります。 たとえば、受注登録では、受注ヘッダ、受注明細、在庫引当、操作ログなどを同時に更新することがあります。 このような処理では、途中の一部だけが成功するとデータ不整合が発生します。
トランザクションが必要な例
- 受注ヘッダと受注明細を同時に登録する
- 請求データ登録と売上データ更新を同時に行う
- 在庫を減らし、出荷データを登録する
- ユーザー登録と権限登録を同時に行う
- 振込元と振込先の残高を同時に更新する
ADO.NETでのトランザクション例
Using connection As New SqlClient.SqlConnection(connectionString)
connection.Open()
Using transaction As SqlClient.SqlTransaction = connection.BeginTransaction()
Try
Using command As New SqlClient.SqlCommand()
command.Connection = connection
command.Transaction = transaction
command.CommandText = "INSERT INTO Orders (OrderDate) VALUES (@OrderDate)"
command.Parameters.Clear()
command.Parameters.AddWithValue("@OrderDate", DateTime.Now)
command.ExecuteNonQuery()
command.CommandText = "UPDATE Stocks SET Quantity = Quantity - @Quantity WHERE ProductId = @ProductId"
command.Parameters.Clear()
command.Parameters.AddWithValue("@Quantity", 1)
command.Parameters.AddWithValue("@ProductId", 100)
command.ExecuteNonQuery()
End Using
transaction.Commit()
Catch ex As Exception
transaction.Rollback()
Throw
End Try
End Using
End Using
トランザクションを使う場合、CommandにTransactionを設定する必要があります。 設定し忘れると、そのCommandだけトランザクションの対象外になる可能性があるため注意が必要です。
トランザクション設計のポイント
- 複数のDB更新をひとつの業務処理として扱う場合に使用する
- CommitとRollbackを明確に制御する
- トランザクション範囲を必要以上に広げない
- ユーザー操作待ちをトランザクション中に行わない
- 外部API通信やファイル出力を含める場合は整合性設計に注意する
トランザクションはデータ整合性を守るために重要ですが、範囲が広すぎるとロック時間が長くなり、 他の処理に影響を与える可能性があります。 必要最小限の範囲で利用することが重要です。
4. パラメータ化クエリ
パラメータ化クエリとは、SQL文に値を直接連結するのではなく、 パラメータとして値を渡す方法です。
DB連携では、ユーザーが入力した値をSQLに使用する場面が多くあります。 このとき、文字列連結でSQLを組み立てると、SQLインジェクションや構文エラーの原因になります。
悪い例:文字列連結でSQLを作成する
Dim sql As String = "SELECT * FROM Users WHERE UserName = '" & txtUserName.Text & "'"
このようなコードは非常に危険です。 ユーザー入力にシングルクォートやSQL文が含まれていた場合、SQLの意味が変わる可能性があります。
良い例:パラメータ化クエリを使う
Dim sql As String = "SELECT * FROM Users WHERE UserName = @UserName"
Using command As New SqlClient.SqlCommand(sql, connection)
command.Parameters.AddWithValue("@UserName", txtUserName.Text)
Using reader = command.ExecuteReader()
While reader.Read()
' 読み取り処理
End While
End Using
End Using
パラメータ化クエリを使うことで、ユーザー入力はSQL文そのものではなく値として扱われます。 これにより、SQLインジェクション対策になります。
AddWithValueの注意点
AddWithValueは簡単に使えますが、型推論によって意図しないDB型として扱われることがあります。 特に文字列長、Decimal、DateTimeなどでは、明示的に型を指定した方が安全です。
Dim parameter As New SqlClient.SqlParameter("@UserName", SqlDbType.NVarChar, 50)
parameter.Value = userName
command.Parameters.Add(parameter)
業務システムでは、パフォーマンスやインデックス利用に影響する場合があるため、 重要なSQLではパラメータの型とサイズを明示することが望ましいです。
パラメータ化クエリのメリット
- SQLインジェクションを防げる
- 文字列中のシングルクォートを安全に扱える
- 日付や数値の形式問題を減らせる
- SQLの可読性が上がる
- 実行計画の再利用が期待できる
5. SQLインジェクション対策
SQLインジェクションとは、ユーザー入力を悪用してSQL文の意味を改変し、 不正なデータ取得、改ざん、削除などを行う攻撃手法です。
業務システムでは、顧客情報、社員情報、売上情報、個人情報など重要なデータを扱うことが多いため、 SQLインジェクション対策は必須です。
危険なSQLの例
Dim sql As String = "SELECT * FROM Users WHERE LoginId = '" & loginId & "' AND Password = '" & password & "'"
このようなSQLでは、入力値によって条件式が改変される危険があります。 たとえば、loginIdに不正なSQL断片を入力されると、認証を回避される可能性があります。
安全なSQLの例
Dim sql As String = "SELECT * FROM Users WHERE LoginId = @LoginId AND PasswordHash = @PasswordHash"
Using command As New SqlClient.SqlCommand(sql, connection)
command.Parameters.Add("@LoginId", SqlDbType.NVarChar, 50).Value = loginId
command.Parameters.Add("@PasswordHash", SqlDbType.NVarChar, 256).Value = passwordHash
Using reader = command.ExecuteReader()
' 認証処理
End Using
End Using
入力値は必ずパラメータとして渡します。 また、パスワードは平文で保存せず、ハッシュ化した値を比較する必要があります。
SQLインジェクション対策の基本
- SQL文を文字列連結で組み立てない
- 必ずパラメータ化クエリを使う
- ストアドプロシージャでも文字列連結SQLには注意する
- 入力値の形式チェックを行う
- DBユーザーに必要以上の権限を与えない
- エラー詳細をユーザーにそのまま表示しない
SQLインジェクション対策は、単にパラメータ化すれば終わりではありません。 入力チェック、権限設計、エラー制御、ログ監視などを組み合わせて、多層的に対策することが重要です。
6. ストアドプロシージャ連携
ストアドプロシージャは、データベース側に保存された処理です。 複雑なSQL処理や、複数の更新処理、集計処理などをDB側にまとめることができます。
VB.NETからストアドプロシージャを呼び出す場合も、ADO.NETのCommandを利用します。 CommandTypeをStoredProcedureに設定することで、SQL文ではなくストアドプロシージャとして実行できます。
ストアドプロシージャ呼び出しの例
Using connection As New SqlClient.SqlConnection(connectionString)
connection.Open()
Using command As New SqlClient.SqlCommand("usp_RegisterUser", connection)
command.CommandType = CommandType.StoredProcedure
command.Parameters.Add("@UserName", SqlDbType.NVarChar, 50).Value = userName
command.Parameters.Add("@Email", SqlDbType.NVarChar, 100).Value = email
command.ExecuteNonQuery()
End Using
End Using
戻り値を受け取る例
Using command As New SqlClient.SqlCommand("usp_GetUserCount", connection)
command.CommandType = CommandType.StoredProcedure
Dim returnParameter As New SqlClient.SqlParameter("@ReturnValue", SqlDbType.Int)
returnParameter.Direction = ParameterDirection.ReturnValue
command.Parameters.Add(returnParameter)
command.ExecuteNonQuery()
Dim count As Integer = CInt(returnParameter.Value)
End Using
出力パラメータを受け取る例
Using command As New SqlClient.SqlCommand("usp_GetUserName", connection)
command.CommandType = CommandType.StoredProcedure
command.Parameters.Add("@UserId", SqlDbType.Int).Value = userId
Dim nameParameter As New SqlClient.SqlParameter("@UserName", SqlDbType.NVarChar, 50)
nameParameter.Direction = ParameterDirection.Output
command.Parameters.Add(nameParameter)
command.ExecuteNonQuery()
Dim userName As String = CStr(nameParameter.Value)
End Using
ストアドプロシージャのメリット
- 複雑なSQL処理をDB側に集約できる
- アプリケーション側のSQL記述を減らせる
- 権限管理を行いやすい
- DB側で処理を最適化しやすい場合がある
- 複数アプリケーションから同じ処理を利用できる
ストアドプロシージャの注意点
- 業務ロジックがDB側に偏りすぎると保守しにくい
- アプリケーションとDBの責務分担を明確にする必要がある
- バージョン管理やデプロイ手順を整備する必要がある
- 動的SQLを使う場合はSQLインジェクションに注意する
- DB依存が強くなり、移行しにくくなる場合がある
ストアドプロシージャは強力ですが、すべての業務ロジックをDB側に置くべきではありません。 アプリケーション側とDB側の責務を整理し、保守しやすい境界を設計することが重要です。
7. DataSet / DataTableの設計
DataSetとDataTableは、メモリ上で表形式データを扱うためのADO.NETの主要なクラスです。 Windows FormsのDataGridViewと組み合わせて使われることも多く、業務アプリケーションでは非常によく登場します。
DataTableとは
DataTableは、メモリ上に1つの表を表現するオブジェクトです。 列情報、行データ、制約、変更状態などを保持できます。
Dim table As New DataTable("Users")
table.Columns.Add("UserId", GetType(Integer))
table.Columns.Add("UserName", GetType(String))
table.Columns.Add("Email", GetType(String))
Dim row As DataRow = table.NewRow()
row("UserId") = 1
row("UserName") = "Tanaka"
row("Email") = "tanaka@example.com"
table.Rows.Add(row)
DataSetとは
DataSetは、複数のDataTableをまとめて保持できるオブジェクトです。 テーブル間のリレーションも定義できます。
Dim dataSet As New DataSet()
Dim usersTable As New DataTable("Users")
Dim ordersTable As New DataTable("Orders")
dataSet.Tables.Add(usersTable)
dataSet.Tables.Add(ordersTable)
DataTableをDataGridViewに表示する
Dim table As New DataTable()
Using connection As New SqlClient.SqlConnection(connectionString)
Using command As New SqlClient.SqlCommand("SELECT UserId, UserName, Email FROM Users", connection)
Using adapter As New SqlClient.SqlDataAdapter(command)
adapter.Fill(table)
End Using
End Using
End Using
DataGridView1.DataSource = table
DataTableはDataGridViewとの相性が良く、検索結果一覧やマスタメンテナンス画面などで利用しやすいです。
DataTable利用時の注意点
- 大量データをすべて読み込むとメモリ使用量が増える
- 列名を文字列で参照するため、タイポに弱い
- 型安全性はList(Of T)より低い
- 業務ロジックをDataRow操作に直接書きすぎると保守しにくい
- 画面表示用と業務処理用のデータ構造を分ける判断が必要
DataTableとList(Of T)の使い分け
| 項目 | DataTable | List(Of T) |
|---|---|---|
| 型安全性 | 低め | 高い |
| DataGridView連携 | 簡単 | 可能だが設計が必要 |
| 業務ロジックとの相性 | やや弱い | 強い |
| 既存VB資産との相性 | 高い | 設計次第 |
| 保守性 | 列名文字列に注意 | クラス定義により高めやすい |
既存のWindows Forms業務アプリではDataTableが多く使われます。 一方、新規設計や業務ロジック中心の実装では、EntityやDTOを定義し、List(Of T)で扱う方が保守性が高くなる場合があります。
DataTableをDTOへ変換する例
Public Function ConvertToUsers(table As DataTable) As List(Of UserDto)
Dim users As New List(Of UserDto)()
For Each row As DataRow In table.Rows
Dim user As New UserDto With {
.UserId = CInt(row("UserId")),
.UserName = CStr(row("UserName")),
.Email = CStr(row("Email"))
}
users.Add(user)
Next
Return users
End Function
DataTableをそのまま全層で使い回すのではなく、 必要に応じてDTOやEntityへ変換することで、型安全性と保守性を高められます。
8. 接続プーリング
接続プーリングとは、データベース接続を毎回完全に作成・破棄するのではなく、 再利用可能な接続をプールしておく仕組みです。
DB接続の確立は比較的コストの高い処理です。 ADO.NETでは、同じ接続文字列を使用する場合、接続プーリングが自動的に利用されます。
接続プーリングの基本
Using connection As New SqlClient.SqlConnection(connectionString)
connection.Open()
' DB処理
End Using
Usingを抜けるとConnectionはDisposeされますが、物理的なDB接続が即座に完全破棄されるとは限りません。 接続プールに戻され、次回同じ接続文字列でOpenされたときに再利用されます。
接続プーリングのメリット
- DB接続確立のコストを削減できる
- アプリケーション全体の応答速度を改善できる
- 短時間に多くのDBアクセスが発生する業務アプリに有効
- ADO.NETが自動で管理するため実装負担が少ない
接続プーリングで注意すべき点
- Connectionを閉じ忘れるとプールが枯渇する
- 接続文字列が微妙に違うと別プールとして扱われる
- 長時間Connectionを保持しない
- トランザクション中に不要な処理を行わない
- 例外発生時も必ずDisposeされるようにする
接続文字列の例
Data Source=.;Initial Catalog=SampleDb;Integrated Security=True;Max Pool Size=100;Min Pool Size=5;
Max Pool Sizeを指定することで、接続プール内の最大接続数を制御できます。 ただし、安易に大きくすればよいわけではありません。 DBサーバー側の負荷や同時接続数も考慮する必要があります。
接続プール枯渇の原因
- ConnectionをClose / Disposeしていない
- DataReaderを閉じ忘れている
- 長時間トランザクションを保持している
- 例外発生時に接続解放処理まで到達していない
- 大量の同時処理に対してDB設計が追いついていない
接続プーリングを正しく活用するには、Usingブロックを徹底し、 DB接続を必要最小限の時間だけ開く設計が重要です。
9. 実務でありがちなDB連携の問題
ここでは、VB.NETの業務システムでよく見られるDB連携上の問題を整理します。
SQLを文字列連結で作っている
ユーザー入力値をSQLに直接連結している場合、SQLインジェクションの危険があります。 また、日付形式やシングルクォートの扱いでエラーが発生しやすくなります。
すべての可変値はパラメータとして渡すべきです。
Connectionを閉じ忘れている
ConnectionをClose / Disposeしていないと、接続プールが枯渇し、 アプリケーション全体でDB接続エラーが発生する可能性があります。
Usingブロックを徹底することで、例外発生時でも確実にリソースを解放できます。
トランザクション制御がない
複数テーブルを更新しているにもかかわらず、トランザクションを使用していない場合、 途中でエラーが発生するとデータ不整合が発生します。
受注登録、請求処理、在庫更新、権限登録など、複数更新を伴う処理ではトランザクションを検討する必要があります。
DataTableを全層で使い回している
DataTableは便利ですが、画面、業務ロジック、DBアクセス層すべてで使い回すと、 列名の文字列参照が増え、型安全性が低下します。
業務処理ではDTOやEntityに変換し、画面表示ではViewModelや表示用DTOを使う設計も検討すべきです。
SELECT * を多用している
SELECT * は便利ですが、不要な列まで取得するためパフォーマンスに悪影響を与える場合があります。 また、テーブル定義変更時に予期しない影響を受けることもあります。
業務処理で必要な列だけを明示的に取得することが望ましいです。
DBアクセス処理が画面イベントに書かれている
ボタンクリックイベント内にSQL実行処理を書くと、画面とDBが密結合になります。 保守性、再利用性、テスト容易性が低下するため、Repositoryクラスへ分離することが重要です。
10. RepositoryパターンによるDBアクセス分離
DBアクセス処理を保守しやすくするためには、Repositoryパターンを利用するのが有効です。 Repositoryは、データの取得や保存を担当するクラスです。 Serviceや画面はSQLの詳細を知らず、Repositoryを通じてデータを扱います。
Entityの例
Public Class User
Public Property UserId As Integer
Public Property UserName As String
Public Property Email As String
End Class
Repositoryインターフェース
Public Interface IUserRepository
Function FindById(userId As Integer) As User
Function FindAll() As List(Of User)
Sub Insert(user As User)
Sub Update(user As User)
Sub Delete(userId As Integer)
End Interface
Repository実装例
Public Class UserRepository
Implements IUserRepository
Private ReadOnly _connectionString As String
Public Sub New(connectionString As String)
_connectionString = connectionString
End Sub
Public Function FindById(userId As Integer) As User Implements IUserRepository.FindById
Using connection As New SqlClient.SqlConnection(_connectionString)
connection.Open()
Dim sql As String = "SELECT UserId, UserName, Email FROM Users WHERE UserId = @UserId"
Using command As New SqlClient.SqlCommand(sql, connection)
command.Parameters.Add("@UserId", SqlDbType.Int).Value = userId
Using reader = command.ExecuteReader()
If reader.Read() Then
Return New User With {
.UserId = CInt(reader("UserId")),
.UserName = CStr(reader("UserName")),
.Email = CStr(reader("Email"))
}
End If
End Using
End Using
End Using
Return Nothing
End Function
Public Function FindAll() As List(Of User) Implements IUserRepository.FindAll
Dim users As New List(Of User)()
Using connection As New SqlClient.SqlConnection(_connectionString)
connection.Open()
Dim sql As String = "SELECT UserId, UserName, Email FROM Users"
Using command As New SqlClient.SqlCommand(sql, connection)
Using reader = command.ExecuteReader()
While reader.Read()
users.Add(New User With {
.UserId = CInt(reader("UserId")),
.UserName = CStr(reader("UserName")),
.Email = CStr(reader("Email"))
})
End While
End Using
End Using
End Using
Return users
End Function
Public Sub Insert(user As User) Implements IUserRepository.Insert
Using connection As New SqlClient.SqlConnection(_connectionString)
connection.Open()
Dim sql As String = "INSERT INTO Users (UserName, Email) VALUES (@UserName, @Email)"
Using command As New SqlClient.SqlCommand(sql, connection)
command.Parameters.Add("@UserName", SqlDbType.NVarChar, 50).Value = user.UserName
command.Parameters.Add("@Email", SqlDbType.NVarChar, 100).Value = user.Email
command.ExecuteNonQuery()
End Using
End Using
End Sub
Public Sub Update(user As User) Implements IUserRepository.Update
' 更新処理
End Sub
Public Sub Delete(userId As Integer) Implements IUserRepository.Delete
' 削除処理
End Sub
End Class
RepositoryにDBアクセスを集約することで、画面やServiceからSQLの詳細を隠蔽できます。 また、IUserRepositoryを使えば、テスト用Repositoryへの差し替えも容易になります。
Repository設計のポイント
- SQLを画面クラスに書かない
- DBアクセスはRepositoryに集約する
- ServiceはRepositoryのインターフェースに依存する
- EntityやDTOに変換して返す
- ConnectionやCommandの管理をRepository内で完結させる
11. 演習課題
演習1:DataReaderを使った検索処理
UsersテーブルからUserId、UserName、Emailを取得し、 List(Of User)として返すRepositoryメソッドを作成してください。
演習2:パラメータ化クエリによる検索
ユーザー名を検索条件として受け取り、 部分一致検索を行うSQLをパラメータ化クエリで実装してください。 文字列連結でSQLを作成しないようにしてください。
演習3:トランザクション制御
注文ヘッダと注明細を登録する処理を作成し、 どちらかの登録に失敗した場合はすべてRollbackされるように実装してください。
演習4:DataTableをDataGridViewに表示する
DataAdapterを使って検索結果をDataTableに読み込み、 DataGridViewに表示する画面を作成してください。
演習5:Repositoryパターンへの分離
ボタンクリックイベント内に書かれているSQL実行処理を、 Repositoryクラスへ分離してください。 画面側はRepositoryまたはServiceを呼び出すだけの構成にしてください。
12. まとめ
本章では、VB.NETからデータベースを安全かつ効率的に扱うためのADO.NETの高度活用について学習しました。
ADO.NETでは、Connection、Command、DataReader、DataAdapter、DataSet、DataTableなどのクラスを使ってDB操作を行います。 それぞれの役割を理解し、用途に応じて使い分けることが重要です。
ConnectionはDB接続を管理し、CommandはSQLやストアドプロシージャを実行します。 DataReaderは高速な逐次読み取りに向いており、DataAdapterはDataTableやDataSetへの読み込みに適しています。
複数のDB更新を伴う業務処理では、トランザクション制御が欠かせません。 途中でエラーが発生した場合にRollbackできるように設計することで、データ不整合を防げます。
また、SQLを文字列連結で組み立てることは避け、必ずパラメータ化クエリを使用する必要があります。 これはSQLインジェクション対策だけでなく、日付や文字列、数値の扱いを安全にするうえでも重要です。
ストアドプロシージャを利用することで、複雑なSQL処理をDB側に集約できます。 ただし、業務ロジックをDB側に寄せすぎると保守性が低下するため、アプリケーション側との責務分担を明確にする必要があります。
DataSetやDataTableは、Windows FormsのDataGridViewと相性が良く、業務アプリケーションでよく利用されます。 一方で、型安全性や保守性の観点ではDTOやEntity、List(Of T)を活用した設計も検討すべきです。
接続プーリングはADO.NETが自動的に管理する強力な仕組みですが、 ConnectionやDataReaderを閉じ忘れるとプール枯渇の原因になります。 Usingブロックを徹底し、DB接続を必要最小限の時間だけ開くことが重要です。
本章のゴールは、VB.NETからDBを操作できるようになることではなく、 安全性、性能、保守性を考慮したDB連携を設計・実装できるようになることです。 この知識は、次章以降の例外処理、ログ設計、障害解析、テスト設計、レガシーシステム改善にも直結します。