さて、データテーブルの内容がうまく更新できたら、次はその変更内容をデータベースに反映する。
前回では、データテーブルにレコードを1行追加し、それをデータアダプタのUpdateメソッドによりデータベースに反映した。データテーブルに新しいレコードが含まれている場合には、InsertCommandプロパティにセットされたSqlCommandオブジェクトが利用され、このinsert用のSqlCommandオブジェクトにはあらかじめinsert文を自前で設定しておく必要があった。あるいは、SqlCommandBuilderクラスにより自動生成したSqlCommandオブジェクトを利用してもレコードの追加をデータベースに反映できた。
今回のテーブルの更新でもやるべきことは同じである。あらかじめupdate文をセットしたSqlCommandオブジェクトをデータアダプタのUpdateCommandプロパティにセットしておけば、Updateメソッドを呼び出すだけで、書き換えられたデータテーブル内のレコードに対して、そのupdate文が実行される。あるいは、SqlCommandBuilderクラスによりupdate文を自動生成することもできる。
ここではまず、自前でupdate文を用意する前者の方法により、Updateメソッドを利用してみよう。取りあえずやりたいことが、pub_nameカラムが書き換えられたデータテーブル内のレコードをデータベースに反映することだとすると、用意するupdate文としては次のようなものが考えられる。
UPDATE publishers SET pub_name = @PubName WHERE pub_id = @PubID
このupdate文を使用して、変更されたレコードをデータベースに反映するサンプル・プログラムは次のようになる。前回のページで示したレコード追加のサンプル・プログラム(insertds.cs)と構造はほとんど同じなので、コードについての解説は不要だろう。
// updateds.cs
using System;
using System.Data;
using System.Data.SqlClient;
public class UpdateFromDataSet {
public static void Main() {
string connStr = "Server=(local)\\NetSDK;"
+ "Trusted_Connection=yes;"
+ "database=pubs";
string selectStr
= "SELECT pub_id, pub_name FROM publishers";
// 接続用オブジェクトの作成
SqlConnection conn = new SqlConnection();
conn.ConnectionString = connStr;
// select用コマンド・オブジェクトの作成
SqlCommand selectCmd = new SqlCommand();
selectCmd.Connection = conn;
selectCmd.CommandText = selectStr;
// データアダプタの作成
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = selectCmd;
// データセットへの読み込み
DataSet ds = new DataSet();
da.Fill(ds, "publishers");
DataTable dt = ds.Tables["publishers"];
// 主キーの設定
dt.PrimaryKey = new DataColumn[] { dt.Columns["pub_id"] };
// データの更新
DataRow targetRow;
targetRow = dt.Rows.Find("0736");
targetRow["pub_name"] = "新月書店";
targetRow = dt.Rows.Find("1756");
targetRow["pub_name"] = "ラモーナ出版";
// update用コマンド・オブジェクトの作成
string updateStr
= "UPDATE publishers"
+ " SET pub_name = @PubName WHERE pub_id = @PubID";
SqlCommand updateCmd = new SqlCommand();
updateCmd.Connection = conn;
updateCmd.CommandText = updateStr;
SqlParameter p1 = new SqlParameter();
p1.ParameterName = "@PubId";
p1.SourceColumn = "pub_id";
updateCmd.Parameters.Add(p1);
SqlParameter p2 = new SqlParameter();
p2.ParameterName = "@PubName";
p2.SourceColumn = "pub_name";
updateCmd.Parameters.Add(p2);
da.UpdateCommand = updateCmd;
// データベースの更新
da.Update(ds, "publishers");
}
}
// コンパイル方法:csc updateds.cs
このプログラムでは、pub_idカラムが「0736」と「1756」の2つのレコードを更新している。実際に正しく更新されたかどうかは、第4回で示しているこのページやこのページにあるサンプル・プログラムで簡単に確かめることができる。
データアダプタのUpdateメソッドを呼び出すことにより、このプログラムから最終的に発行される実際のSQL文は次の2つである(名前付きパラメータによる値の受け渡しは省略(展開)している)。
UPDATE publishers SET pub_name = '新月書店' WHERE pub_id = '0736'
UPDATE publishers SET pub_name = 'ラモーナ出版' WHERE pub_id = '1756'
ちなみに、実際にデータベースに対して発行されたSQL文は、前回で紹介したSQL Server 2000に付属の「SQLプロファイラ」で確認できる。
いま示したサンプル・プログラム(updateds.cs)で使用したupdate文では、指定したpub_id値を持ったレコードを、有無を言わせずに更新してしまう。これは、プログラムを実行するユーザーが1人だけの場合には問題なく使用できるが、複数のユーザーが同時に使用する場合には問題が発生する。一番最後の更新が常に優先されてしまうためだ。このような問題は、データベース更新時の「同時実行制御」にかかわるものだ。
■データベース更新のパターンその1
次のような例を考えてみよう。航空機の座席予約システムである。Aさんは出張のために航空機を予約しようと思い、航空会社のWebページを開いた。空席を検索したところ幸運なことに1つだけ残っていた。個人情報を入力し終えて[予約確定]ボタンをクリックしようとしたところに電話がかかってきた。AさんはWebページを開いたままで、電話を取りに行った。
Aさんが電話対応している間に、別のところに住むBさんが同じ便の1つだけ残っていた座席を見つけ、予約を完了した。ところが、電話を終えたAさんが[予約確定]ボタンをクリックすると予約が成功してしまう。Bさんが入力したはずの予約情報はAさんのもので更新されてしまったので、Bさんが予約した情報はどこにも残っていない。
先ほどのupdate文の挙動はまさにこれである。システム上では不整合は発生しないが、ユーザーから見ればかなり不条理なシステムであるといえるだろう。
■データベース更新のパターンその2
このような不条理を回避する1つの方式は、Aさんが空席情報を表示した時点で座席情報をロックしてしまうという方式だ。この場合、後から予約しようとしたBさんが空席情報を表示し、予約を実行しようとしてもエラーとなる。座席情報がロックされているので更新できないないためだ。あるいは、Bさんに対しては最初から「空席なし」と表示してもよいかもしれない。電話を終えたAさんはそのまま予約処理を継続することができる。
しかしこの場合、必ずしもAさんが予約を実行するとは限らない。かかってきた電話の内容が出張中止を伝えるものであれば、Aさんは予約をせずにそのままブラウザを閉じてしまうだろう。そうすると座席が売れ残ってしまう可能性も出てくる。これは航空会社にとっては大きな損失である。
■データベース更新のパターンその3
一般的に利用され、ADO.NETでも全面的に採用されている方式は次のようなものである。予約処理が完了していないAさんの離席中に、空席を検索し予約を実行したBさんは予約が完了する。
友人からのたわいもない電話に出てしまったAさんは、席に戻ってきて[予約確定]ボタンをクリックするが、予約エラーが表示される。あせったAさんはもう一度空席検索をするが、すでに空席はなくなってしまっている。よくあることだ。
データベース的には、Bさんが[予約確定]ボタンをクリックすることにより実行されたupdate文は成功し、Aさんのupdate文はエラーとなるということである。
Copyright© Digital Advantage Corp. All Rights Reserved.