- PR -

DataAdapterのUpdateで更新する場合のトランザクション管理

投稿者投稿内容
Access
ぬし
会議室デビュー日: 2002/04/08
投稿数: 829
投稿日時: 2005-01-23 06:27
引用:

コマンドビルダはあまり使わないほうが無難でしょうか。
まじめに自分でSQL文を生成したほうが安全ですかね。


性能を考慮されるのでしたら、DataAdapterのUpdateメソッドで使用する
INSERT, UPDATE, DELETE用のSQL文をストアドプロシージャに自前で用意
すべきです。

なお、UPDATE, DELETEのSQL文を作成するときは、2重更新を考慮して
楽観的ロックを組み込んでください。

CommandBuilderが自動生成するSQLは、2重更新を回避するために
SQLのWHERE句に列名の条件式が追加されますので効率よくありません。

自前でSQLを作成するときは、列名を羅列するのではなく主キーと
タイムスタンプなどで2重更新を回避します。

私は、タイムスタンプの代わりにConcurrencyID列を追加して
2重更新を回避しています。

それからOLEDBではなく、ODP.NETを利用することをお勧めします。

参考までにOracle用のパッケージ本体部を添付します。
コード:

CREATE OR REPLACE PACKAGE BODY CustomerPackage AS

PROCEDURE InsertCustomersCurrVal(
iCompanyName IN VARCHAR2,
iContactName IN VARCHAR2,
iPhone IN VARCHAR2,
oCustomerID OUT NUMBER) IS
BEGIN
INSERT INTO Customers
(CompanyName, ContactName, Phone)
VALUES (iCompanyName, iContactName, iPhone);
SELECT Customers_CustomerID_Seq.CURRVAL ←シーケンス番号取得
INTO oCustomerID
FROM DUAL;
END InsertCustomersCurrVal;

PROCEDURE UpdateCustomersConcurrencyIDRc(
iCompanyName IN VARCHAR2,
iContactName IN VARCHAR2,
iPhone IN VARCHAR2,
iCustomerID IN NUMBER,
iConcurrencyID IN NUMBER,
oRowCount OUT NUMBER) IS
BEGIN
UPDATE Customers
SET CompanyName = iCompanyName,
ContactName = iContactName,
Phone = iPhone,
ConcurrencyID = ConcurrencyID+1
WHERE CustomerID = iCustomerID
AND ConcurrencyID = iConcurrencyID;
oRowCount := SQL%ROWCOUNT;
END UpdateCustomersConcurrencyIDRc;

PROCEDURE DeleteCustomersConcurrencyIDRc(
iCustomerID IN NUMBER,
iConcurrencyID IN NUMBER,
oRowCount OUT NUMBER) IS
BEGIN
DELETE
FROM Customers
WHERE CustomerID = iCustomerID
AND ConcurrencyID = iConcurrencyID;
oRowCount := SQL%ROWCOUNT;
END DeleteCustomersConcurrencyIDRc;
END CustomerPackage;




_________________
ASP.NETサンプル集(Web Matrix版)

[ メッセージ編集済み 編集者: Access 編集日時 2005-01-23 10:16 ]
Access
ぬし
会議室デビュー日: 2002/04/08
投稿数: 829
投稿日時: 2005-01-23 07:03
引用:

kesさんの書き込み (2005-01-22 17:27) より:
すみません。
やってみましたが、状況変わらずです。



OLEDBで確認しましたが正常に動作しました。
参考までにソースコードを添付します。

コード:
Private Function UpdateRecord(ByVal strCompanyName As String, _
  ByVal strContactName As String, _
  ByVal strPhone As String, _
  ByVal intCustomerID As Integer) As Integer

  Dim intRetValue As Integer = 0
  Dim txn As OleDbTransaction
  Dim dr As DataRow = mdt.Rows.Find(intCustomerID)
  If Not (dr Is Nothing) Then
    dr("CompanyName") = strCompanyName
    dr("ContactName") = strContactName
    dr("Phone") = strPhone
    Try
      mcon.Open()
      txn = mcon.BeginTransaction
      mcb.GetInsertCommand.Transaction = txn
      mcb.GetUpdateCommand.Transaction = txn
      mcb.GetDeleteCommand.Transaction = txn
      mda.Update(mdt)
      txn.Commit()
      intRetValue = 1
    Catch ex As Exception
      If Not (txn Is Nothing) Then
        txn.Rollback()
      End If
      Response.Write(ex.Message.ToString)
    Finally
      mcon.Close()
    End Try
  End If
  Return intRetValue
End Function



Tip:
私は仕事柄、Access, MySQL, SQL Server, Oracleを利用するのですが
connectionstring.com便利ですね!一度、覗いてみてください。
http://www.connectionstrings.com/


_________________
ASP.NET+Ajaxサンプル集 | JavaScript+Ajaxサンプル集
kes
ベテラン
会議室デビュー日: 2004/08/10
投稿数: 67
投稿日時: 2005-01-24 15:16
いろいろとありがとうございます。

書いてくださったソースでやってみたのですが、

mcb.GetInsertCommand.Transaction = txn

の部分で、
「Executeは、コマンドに割り当てられた接続が保留状態であるローカルのトランザクションにあるとき、トランザクション オブジェクトを持つコマンドが必要です。コマンドのTransactionプロパティがまで初期化されていません。」
というエラーになります。よく意味が分かりません。誤字っぽいのもそのままです。

下記のようにしたらうまくいきました。

mda.InsertCommand = mcb.GetInsertCommand
mda.DeleteCommand = mcb.GetDeleteCommand
mda.UpdateCommand = mcb.GetUpdateCommand

txn = mcon.BeginTransaction

mda.InsertCommand.Transaction = txn
mda.DeleteCommand.Transaction = txn
mda.UpdateCommand.Transaction = txn

なんだか分からないけどうまくいくということではちょっと怖いので、
もっと勉強して分かるようになってから使おうと思います。


ありがとうございました。
Access
ぬし
会議室デビュー日: 2002/04/08
投稿数: 829
投稿日時: 2005-01-24 15:32
引用:

下記のようにしたらうまくいきました。

mda.InsertCommand = mcb.GetInsertCommand
mda.DeleteCommand = mcb.GetDeleteCommand
mda.UpdateCommand = mcb.GetUpdateCommand

txn = mcon.BeginTransaction

mda.InsertCommand.Transaction = txn
mda.DeleteCommand.Transaction = txn
mda.UpdateCommand.Transaction = txn




mcon.Open() <-- これを追加すると正常に動作すると思います.
txn = mcon.BeginTransaction

mda.InsertCommand.Transaction = txn
mda.DeleteCommand.Transaction = txn
mda.UpdateCommand.Transaction = txn

mcon.Close()


_________________
ASP.NET+Ajaxサンプル集 | JavaScript+Ajaxサンプル集
kes
ベテラン
会議室デビュー日: 2004/08/10
投稿数: 67
投稿日時: 2005-01-24 15:52
ありがとうございます。

>mcon.Open() <-- これを追加すると正常に動作すると思います.
>txn = mcon.BeginTransaction

Accessさんが書いてくださったソースにはもとからOpenが入っておりましたので、
Opneもやっております。

私のやったやり方ですと、
@Open
AGetInserCommand
BBeginTransaction
CInsertCommand.TransactionにBのTransactionを設定
DUpdate
ECommit
ならうまくいくのですが、
AGetInsertCommandをBBeginTransactionの後に持ってくると、
先ほどのエラーが発生します。
GetxxxxCommandはトランザクション開始の前でないと
だめっぽい感じです。
理由は分かりませんが。

スキルアップ/キャリアアップ(JOB@IT)