プログラムの概要
ACCESS 等のデータベースの操作に不慣れな者でも、簡単 に使用する事が出来るようなデータベースアプリケーショ ンを目指します。 今回は、データグリッドをクリックする事に依り、其のレ コードのフィールドデータを個別にテキストボックスやコ ンボボックスに表示する機能を付加する。此の際、カテゴ リIDや著者IDの様に人間に解り難いデータ形式では無 く、名称で表示する事に依り、操作性を高めて居る。 亦、テキストボックスやコンボボックスで入力したデータ を追加したり、表示された内容に変更を加えて更新したり 表示されたレコードを削除したりする機能を付加する。 VB でデータベースを操作する為の基本と成る機能を盛り 込んで有るので、色々と工夫し、発展させて、操作性の良 いアプリケーションに仕上げて下さい。データベース操作プログラムⅢ
VB 2005 ⑳□ コレクションの利用(ComboBox.Items、ADODB.Recordset.Fields)
□ プロパティの利用(Checked、Items.Count、SelectedIndex、Text、Value) □ メソッドの利用(Execute、Requery、WriteLine、SubString)
□ ステートメントの利用(AddHandler、Call、Exit Sub、Exit For) □ 演算子の利用(AddressOf、Or)
□ 制御構造構文(ループ For ~ Next、条件分岐 Select Case、If ~ Then … End If) □ デバッグ(Debug.WriteLine) 今回の課題項目 □ イベントハンドラの追加(AddHandler) □ ADODB.Recordset(Fields( ).Value、Refresh) □ ADODB.Connection(Open、Execute、Close) □ SQL 文(INSERT 文:レコードを追加する文、DELETE 文:レコードを削除する文) 今回の重点項目 □ タイトルの項目を書き換えた時に削除出来ない不具合を修正する。 □ 本テーブル以外(カテゴリ、著者)のデータも操作(追加・変更・削除)出来る様に変更する。 今回の応用項目
■ プログラムリスト(追加分) ■ ' フォームが読み込まれた時の処理
Private Sub bookshelf_Load( ByVal sender As Object, ByVal e As System.EventArgs ) _ Handles Me.Load ' コネクションとレコードセットのインスタンス生成 Cn = New ADODB.Connection Rs = New ADODB.Recordset ' コネクションの確立(データベースへの接続) Cn.CursorLocation = ADODB.CursorLocationEnum.adUseClient
Cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=data.mdb;" Cn.Open( )
' 選択クエリに依るレコードセットの取得
Rs.Open( "SELECT 本.*,著者.著者名 FROM 本,著者 WHERE 本.著者 ID=著者.ID;", Cn ) ' データグリッドへの連結 dgdBookshelf.DataSource = Rs ' サブルーチンコール Call CboAddItem( ) Call CateUpdate( ) ' イベントハンドラの追加【追加】
AddHandler btnInsert.Click, AddressOf OperationSQL AddHandler btnUpdate.Click, AddressOf OperationSQL AddHandler btnDelete.Click, AddressOf OperationSQL End Sub
' データグリッドがクリックされた時の処理【追加】
Private Sub dgdBookshelf_ClickEvent( ByVal sender As Object, ByVal e As System.EventArgs ) _ Handles dgdBookshelf.ClickEvent Dim I As Integer Dim S As String ' ラジオボタンで『本』が選択されて居る時だけフィールドデータを表示 If radBook.Checked Then
txtID.Text = Rs.Fields( "ID" ).Value & ""
txtTitle.Text = Rs.Fields( "タイトル" ).Value & "" txtPrice.Text = Rs.Fields( "価格" ).Value & "" For I = 0 To ( cboCategory.Items.Count – 1 ) S = cboCategory.Items( I )
If Val( Rs.Fields( "カテゴリ ID" ).Value & "" ) = Val( S.Substring( 0, 3 ) ) Then cboCategory.SelectedIndex = I Exit For End If Next For I = 0 To ( cboWriter.Items.Count – 1 ) S = cboWriter.Items( I )
If Val( Rs.Fields( "著者 ID" ).Value & "" ) = Val( S.Substring( 0, 3 ) ) Then cboWriter.SelectedIndex = I Exit For End If Next End If End Sub 複数のコントロールを同一のイベ ントプロシージャに関連付けるに はAddHandler ステートメントを 使用する。 但し、イベントプロシージャに関 連付けられるプロシージャは、前 以て記述して置く。 前回作成したプログラムを基に追 加する。 データグリッドに表示されて居る 左端の「 」部分をクリックする と、Recordset 内で、其のレコー ドが、カレントレコードと成る。 カレントレコードの各フィールド より其の値を取得して、コントロ ールに表示して居る。 コンボボックスのリストに対応す る数値を格納するItemData プロ パティが廃止された為、リストに ID の値を 3 桁で含めて居る。
' コマンドボタンがクリックされた時の処理【追加】
Private Sub OperationSQL( ByVal sender As Object, ByVal e As System.EventArgs )
Dim IDX As Integer = sender.tag Dim SQL, SC, SW As String
' ラジオボタンで『本』が選択されて居なければプロシージャを脱出 If Not radBook.Checked Then Exit Sub
Select Case IDX Case 0
' ボタン(追加)がクリックされた場合 If txtTitle.Text = "" Then Exit Sub
If cboCategory.SelectedIndex < 0 Then Exit Sub If cboWriter.SelectedIndex < 0 Then Exit Sub
SC = cboCategory.Items( cboCategory.SelectedIndex ) : SC = Trim( SC.Substring( 0, 3 ) ) SW = cboWriter.Items( cboWriter.SelectedIndex ) : SW = Trim( SW.Substring( 0, 3 ) ) SQL = "INSERT INTO 本(タイトル,価格,購入日,カテゴリ ID,著者 ID) VALUES(" SQL &= "'" & txtTitle.Text & "',"
SQL &= txtPrice.Text & "," SQL &= "'" & Now & "',"
SQL &= SC & "," & SW & ");" Case 1
' ボタン(変更)がクリックされた場合
If cboCategory.SelectedIndex < 0 Then Exit Sub If cboWriter.SelectedIndex < 0 Then Exit Sub
SC = cboCategory.Items( cboCategory.SelectedIndex ) : SC = Trim( SC.Substring( 0, 3 ) ) SW = cboWriter.Items( cboWriter.SelectedIndex ) : SW = Trim( SW.Substring( 0, 3 ) ) SQL = "UPDATE 本 SET "
SQL &= "タイトル='" & txtTitle.Text & "'," SQL &= "価格=" & txtPrice.Text & "," SQL &= "カテゴリ ID=" & SC & "," SQL &= "著者 ID=" & SW & " "
SQL &= "WHERE ID=" & txtID.Text & ";" Case 2
' ボタン(削除)がクリックされた場合
SQL = "DELETE FROM 本 WHERE タイトル='" & txtTitle.Text & "';" End Select ' デバッグの為にイミディエイトウィンドウに SQL 文を表示 Debug.WriteLine( SQL ) ' クエリを実行 Cn.Execute( SQL ) Call CateUpdate( ) ' 表示データを更新する為にクエリの再実行 Rs.Requery( ) End Sub 基本的にVB のコードは 1 行毎でしか記述する事が出来ないが、1行のコードが長い場合は、行継 続文字(スペースとアンダースコアの組合せ)を使用する事で、複数行に分割して入力する事も出 来る。但し、行継続文字に続けて、同一行にコメントを入力する事は出来ない。行継続文字を使用 すると、画面に表示する時や印刷した時に、読み易いコードを記述する事が出来る。 頻繁に行う処理は、名前を付け、 一纏めに仕て置き、其の処理を必 要とする箇所よりコール(呼出) する事で、対象のコードを実行す る事が出来る。 フィールドに設定する値は、文字 列型の場合は、シングルコーテー ションでクォートし、数値型の場 合は、其の侭記述する。 長い文は、適当な処で区切り、複 数行で結合して行くと、コードが 見易く成る。
ADODB.Recordset の Open メソッド (再掲) カーソルを開くメソッド Recordset.Open( 引数1,引数2,引数3,引数4,引数5 ) ベーステーブルからのレコード、クエリ結果、又は、以前に保存されたRecordset を表すカ ーソルを開く事が出来る。 引数1には、実行するSQL ステートメント、テーブル名等を指定し、省略する事が出来る。 引数2には、有効なConnection オブジェクト変数名を指定し、省略する事が出来る。 引数3には、Recordset を開く際にプロバイダが使うカーソルタイプを指定し、省略する事が出来る。 引数4には、Recordset を開く際にプロバイダが使うロックの種類を指定し、省略する事が出来る。 引数5には、プロバイダが引数1を評価する方法を示す定数を指定し、省略する事が出来る。 ADODB.Recordset の Requery メソッド クエリを再実行してRecordset オブジェクトのデータを更新するメソッド Recordset.Requery( 引数 ) オブジェクトの基に成るクエリ(CommandText プロパティに設定されて居るクエリ)を再 実行してRecordset オブジェクトのデータを更新する。 引数には、此の操作が作用するオプションを示すビットマスクを指定し、省略可能で有る。此のパラメ ー タ が adAsyncExecute に 設 定 さ れ て 居 る 場合 、 此 の 操 作 は 非 同 期で 実 行 さ れ 、 完 了 すると RecordsetChangeComplete イベントが発生する。 Requery メソッドを使用して、元のコマンドを再実行してデータをもう一度格納する事に依り、データ ソースからRecordset オブジェクトの内容全体を更新する。此のメソッドの呼出は、Close 及び Open メソッドを連続して呼び出すのと同じ有る。カレントレコードの編集中、又は、新規レコードを追加し て居る時に此のメソッドを呼び出すと、エラーが発生する。 Recordset オブジェクトが開いて居る間、カーソルの属性を定義するプロパティ(CursorType、 LockType、MaxRecords 等)は読み取り専用に成って居る。此の為、Requery メソッドでは、現在の カーソルしか更新する事は出来ない。カーソルのプロパティを変更して結果を参照するには、Close メ ソッドを使用してプロパティをもう一度読み取り/書き込み専用にする必要が有る。此の操作の後、プロ パティ設定を変更してOpen メソッドを呼び出し、カーソルを再度開く。 ADODB.Recordset の Close メソッド (再掲) 開いて居るオブジェクト、及び、依存する総てのオブジェクトを閉じるメソッド Recordset.Close( ) Connection オブジェクト、又は、Recordset オブジェクトを閉じて、関連するシステムリソ ースを解放する。 オブジェクトを閉じてもメモリからは削除されず、プロパティ設定を変更してもう一度開く事が出来る。 メモリからオブジェクトを完全に削除するには、オブジェクト変数をNothing に設定する。 機 能 書 式 解 説 機 能 書 式 解 説 機 能 書 式 解 説
ADODB.Recordset の Fields コレクション
Recordset オブジェクトの総ての Field オブジェクトが含まれるコレクション
Recordset オブジェクトは、Recordset 内の列に対応する Field オブジェクトで構成される Fields コレクションを持つ。 Fields コレクションは、コレクション内のオブジェクト数(即ち、フィールドの数)を示す Count プ ロパティが有る。 コレクションのメンバ(要素)は、常に0 から順に番号が割り当てられる為、0 から Count プロパティ より1 小さい値迄のループを使用すると、総てのメンバにアクセスする事が出来る。 猶、Count プロパティが 0 の場合、コレクションにはオブジェクトが含まれて居ない事を意味する。 各々のメンバ(Field オブジェクト)は、下記の様に、フィールド名、又は、序数を用いて表わす事が 出来る。 フィールド名使用:Recordset.Fields( "タイトル" ) 序 数 使 用:Recordset.Fields( 1 ) 序数は、SELECT 文で指定したフィールドの順番(アスタリスク * で総てのフィールドを指定した場 合は、テーブルに定義されて居る順番)に割り当てられ、最初の要素は0 で有る。 ADODB.Recordset の Field オブジェクト 共通のデータ型を持つデータの列を表すオブジェクト
Recordset オブジェクトは、Recordset 内の列に対応する Field オブジェクトで構成される Fields コレクションを持つ。 Field オブジェクトの Value プロパティを使用して、カレントレコードのデータを設定、又は、参照す る事が出来る。但し、Field オブジェクトのコレクション、メソッド、プロパティの中には、プロバイ ダが公開する機能に依っては使用できない物も有る。 一般にField オブジェクトのコレクション、メソッド、プロパティを使用して、次の操作が可能で有る。 ・Name プロパティを使用して、フィールド名を参照する。 ・Value プロパティを使用して、フィールド内のデータ表示や変更を行う。 ・Type、Precision、NumericScale プロパティを使用して、フィールドの基本特性を参照する。 ・DefinedSize プロパティを使用して、宣言したフィールドサイズを参照する。 ・ActualSize プロパティを使用して、与えられたフィールド内の実際のデータサイズを参照する。 ・Attributes プロパティ、Properties コレクションを使用して、与えられたフィールドでサポートされ て居る機能の種類を識別する。 ・AppendChunk、GetChunk メソッドを使用して、長バイナリ型や文字型データを含むフィールドの 値を操作する。 ・プロバイダがバッチ更新をサポートして居る場合、OriginalValue、UnderlyingValue プロパティを 使用して、フィールド値の矛盾を解決する。 猶、総てのメタデータプロパティ(Name、Type、DefinedSize、Precision、NumericScale)は、Field オブジェクトのRecordset を開く前に利用出来る。動的にフォームを作成する場合に便利で有る。 機 能 解 説 機 能 解 説
Debug オブジェクトに依るデバッグ コードのデバッグに使用するメソッドとプロパティのセットを提供するオブジェクト Debug クラスのメソッドを使用し、デバッグ情報を出力し、アサーションと照らし合わせて 論理チェックを行う事で、パフォーマンスや出荷製品のサイズに影響を与える事無くコード の信頼性を高める事が出来る。 此のオブジェクトには、Write、WriteLine、WriteIf、WriteLineIf の各メソッドが用意されて居り、出 力ウィンドウに、変数等に格納されて居る値を出力する事に依り、其の値を確認する事が出来る。 猶、Visual Basic.NET 以降のエディタでは、実行時に既定で画面の下部に表示される『自動変数』の タブに、其の時の変数の値が表示され、亦、『デバッグ』メニューの『クイックウォッチ』で、任意の 変数や配列の値を確認する事も出来る。 Debug オブジェクトの WriteLine メソッドに依るデバッグ情報の出力 デバッグに付いての情報をListeners コレクションのトレースリスナに書き込メソッド Debug.WriteLine( 引数 ) Listeners コレクションのトレースリスナにメッセージを出力し、行終端記号(改行)を出 力する。 引数には、変数名、オブジェクト名、文字列を指定し、省略可能で有る。 一般的には、変数名を指定して、其の時点で、当該変数に格納されて居る値を確認する事に使用する。 猶、引数を省略した場合は、行終端記号(改行)而巳が出力される。 行終端記号を出力しない場合は、Write メソッドを使用する。Write メソッドでは、行終端記号は出力 されず、次の出力は、同一行に続けて出力される。 亦、条件に応じて、出力するか出力しないかを制御するには、WriteIf メソッドや WriteLineIf メソッ ドを使用する事も出来る。但し、WriteIf メソッドや WriteLineIf メソッドの代わりに If...Then ステー トメントを使用する事に依り、アプリケーション導入に依るパフォーマンスの低下を最小限に留める事 が出来る為、余り使用する事は無い。
猶、Debug オブジェクトには、条件をチェックし、其の条件が false の場合に、メッセージを表示する Assert メソッドも用意されて居る。Type パラメータが有効か何うかをチェックし、Type と仕て nul 参照(Visual Basic では Nothing)が渡された場合は、Assert がメッセージを出力する例を次に示す。 Public Shared Sub MyMethod(T As Type)
Debug.Assert( Not (T Is Nothing), "オブジェクトを取得出来ません!") End Sub 'MyMethod
亦、Debug オブジェクトには、デバッグ時(エディタで実行した時)に而巳、任意のエラーメッセージ を出力するFail メソッドも用意されて居る。 此等のデバッグ機能を旨く利用して、プログラム中の間違い(バグ)を素早く発見し、開発効率を向上 させる様に努める事が望ましい。 機 能 解 説 機 能 書 式 解 説
AddHandler ステートメントに依るイベントハンドラの追加 (再掲) イベントをイベントハンドラに関連付けるステートメント AddHandler 引数1 AddressOf 引数2 コントロールのイベントと其れを処理するイベントプロシージャを関連付ける。 引数1には、処理するイベントの名前を指定し、省略する事は出来ない。 引数2には、イベントを処理するプロシージャの名前を指定し、省略する事は出来ない。 New キーワードに依りコードで生成したコントロールは、何のイベントプロシージャにも関連付けられ て居ない為、イベント処理が行われない。其の為、AddHandler ステートメントを使用して、既存のイ ベントプロシージャと関連付ける必要が有る。 イベントハンドラとは、対応するイベントが発生した時に呼び出されるプロシージャで有る。任意の有 効なサブルーチンをイベントハンドラと仕て使用する事が出来る。但し、イベントハンドラはイベント ソース(イベントの発生元)に値を返す事が出来ない為、関数(Function プロシージャ)をイベント ハンドラと仕て使用する事は出来ない。 ※ Visual Basic では、イベントハンドラに対して、イベント発生元コントロール名_イベント名と謂う 形式でイベントハンドラ名を付けて居る。例えば、button1 と謂う名前のボタンの Click イベントは Sub button1_Click と謂う名前に成る。従って、独自のイベントのイベントハンドラを定義する場合 は、此の標準名前付け規則を使用する事が推奨されるが、名前付け規則の使用は必須ではなく、任 意の有効なサブルーチン名を使用する事も出来る。 AddHandler ステートメントと RemoveHandler ステートメントを使用すると、プログラムの実行中に 任意の時点でイベント処理の開始や停止を行う事が出来る。 AddressOf 演算子に依るプロシージャのアドレスの取得 特定のプロシージャを参照するプロシージャデリゲートインスタンスを作成する演算子 AddressOf 引数 引数で指定されたプロシージャ(関数、サブルーチン)を参照するデリゲートが作成される。 引数には、新規作成されたプロシージャデリゲートに依って参照されるプロシージャを指定し、必ず指 定する。 AddressOf 演算子を使用して、ボタンの Click イベントを処理するデリゲートを指定する例を次に示す。 Public Sub ButtonClickHandler( ByVal sender As Object, e As System.EventArgs )
' … End Sub
Public Sub New( )
AddHandler Button1.Click, AddressOf ButtonClickHandler ' … End Sub 機 能 書 式 解 説 機 能 書 式 解 説
ADO と ADO.NET
Visual Basic.NET では、従来の ADO を従来の方法で使用する事も出来るが、通常、データベースにア クセスする為にADO.NET を使用する。此処では、Windows 上で動作するアプリケーションの開発に 必要な知識を中心に、ADO と ADO.NET の差異を説明する。
Visual Basic 6.0 では、データベースにアクセスする為に ADO(ActiveX Data Object)を使用するが ADO.NET は、此れを.NET Framework 用に進化させた物で有り、両者は、データアクセスの概念に大 きな差異が有る。
■ データアクセス用のコンポーネント
ADO
Visual Basic 6.0 では、データアクセス用のコンポーネントに ADO データコントロール(ADODC)が 有り、下記の機能を提供して居る。 ・データベースに接続する。 ・データベースから必要なデータを取得する。 ・フォーム上のコントロールとデータベースを連結する。 ADO.NET ADO.NET には、ADO の様な便利なコンポーネントは無い。コネクションを使用してデータベースに 接続し、データアダプタを使用して必要なデータを取得する。 ■ データの保持方法 ADO ADO では、取得したデータをレコードセットで管理する。レコードセットは、メモリ上に保持した仮 想テーブルの様な物で有る。 ADO.NET ADO.NET では、データセットに依り、データベースと同じ物をメモリ上に保持する事が出来る。デー タセットには、複数のテーブルの構造や、主キーやリレーションシップ等の属性を定義する事が出来る。 亦、実行時には、取得したデータを管理する事も出来る。 ■ レコードの扱い方 ADO ADO のレコードセットに含まれるレコードは Bookmark プロパティに依り管理されて居り、孰れかの レコードが必ず選択された状態に成って居る。因みに、レコードセットを取得した初期状態では、先頭 のレコードが選択された状態に成って居る。此のレコードをカレントレコードと呼び、亦、MoveNext メソッドやMovePrevious メソッド等に依り、カレントレコードを移動する事が出来る。 ADO.NET ADO.NET には、カレントレコードと謂う概念は無い。其の為、レコードを移動する為のメソッドも無 い。データセットに保持されたデータは、行のコレクションと仕て扱われ、各レコードは、配列の要素 番号を使用して識別する。
■ 接続方法 ADO ADO では、データベースに常時接続した状態で使用するのが基本で有る。其の為、常に最新のデータ を取得し、更新する事も可能で有るが、実際には、データの転送時以外は、データベースに接続して居 る必要が無い場合が多い。亦、データベースへの接続を維持する事で、システムリソースを消費し、パ フォーマンスが低下すると謂う負の側面も有る。 ADO.NET ADO.NET では、データセットに依り、データベースと粗同じ物をメモリ上に保持する事が出来るので データベースに接続する必要が有るのは、データセットにデータを取得する時と、データセットで変更 した内容をデータベースに転送する時丈で有る。其れ以外の時はデータベースの接続を解除し、データ セットを利用してデータベース操作を行う。 ■ ADO と ADO.NET の使い分け 新規にアプリケーションを開発する時は、ADO.NET を利用すると、豊富なツールを利用して、効率良 くアプリケーションを開発する事が出来る。ADO.NET では、データベースと常時接続する必要が無い 為、システムの負荷を軽減する事が出来る。併し、非接続型故に、大量のレコードの即時更新や、常に 最新の情報が必要な場合には、不適格で有り、此の様な場合は、常時接続型の ADO を利用すると良 い。.NET Framework でも、ADO は、サポートされて居る。
データベース フォーム フォーム データセット データベース データアダプタ コネクション
オブジェクト指向 嘗てのプログラムはシンプルなシステムの上に成り立って居た。併し、近日では、GUI(グラ フィカルユーザインターフェイス:マウスで視覚的に操作出来る仕組み)の OS(オペレーシ ョンシステム:Windows98、2000、XP、MacOS 等)が発達し、より簡単に、より複雑な 処理を行える様に成った。其れに伴い、プログラミングの手法も其れに沿う必要が有った。 ・ 開発スピードの向上 ・ 様々な機能を有するソフト開発が出来る事。 ・ 過去作業の再利用 上記の条件を満たすプログラミングの手法として『オブジェクト指向』と謂う概念が編み出 された。簡単に謂うと、『部品と部品を組み合わせてひとつの物を作る』と謂う物で有る。 VisualBasic に限らず、Java、C++、Delphi でも取り入れられて居る概念で有り、大方、考 え方に違いは無い。 概 念 図 解 オブジェクト(Object) 若しくはコントロール(Control) フォームやラベルやテキストボックス等 を指します。物体と謂う意味です。 プロパティ(Property) 私には特徴が有ります。 例えば、 名前:りんごちゃん 高さ:5cm 幅 :5cm 色 :赤色 等々 必要に応じて変更して下さい。 イベント(Event) 皆さんがマウスで私をクリックしたりする と、出来事が起こります。 例えば、
Private Sub りんごちゃん_Click() プログラム End Sub 必要に応じて何をするか言って (プログラムして)下さい。 メソッド(Method) 命令して頂ければ、 行動します。 例えば、 「動きなさい」と言って下さい。 りんごちゃん.Move いずこへ オブジェクト 1から作成する事も出来るが、既存の物に関しては使用する。VB では様々なコントロールが用 意されて居る。 プロパティ コントロールには属性が有り、デザイン上、コード上で変更、参照する事が出来る。其の数、 種類はコントロールに依り異なる。 イベント GUI では様々な操作が有る。例えば、クリック、ダブルクリック、ドラッグドロップ等。必要 に応じてイベント内にプログラムする。イベントドリブン(分割)方式と言う。 メソッド プロパティと区別が付き難いが、『=』で結ばれて居ないのが特徴。オブジェクトに対して『命 令』すると覚えると良い。