MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
MFCとデータベース - CRUDの実行のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
MFCとデータベース - CRUDの実行
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == MFCにおけるデータベースアクセスは、<code>CDatabase</code>クラスと<code>CRecordset</code>クラスを中心に構築される。<br> これらのクラスはODBCを介して、様々なデータベースと通信を行う。<br> <br> * CDatabaseクラス *: データベース接続を管理する。 * CRecordsetクラス *: クエリの実行結果や操作を扱う。 <br> データベース接続を開始するには、CDatabaseクラスのインスタンスを生成して、OpenメソッドまたはOpenExメソッドを実行する。<br> 接続文字列には、DSN、ユーザ名、パスワード等の認証情報を含めることができる。<br> <br> データの取得ではCRecordsetクラスを通じて行う。 CRecordsetクラスは、特定のテーブルやクエリ結果に対応しており、レコードの追加、更新、削除等のCRUD操作が可能である。<br> また、フィールドバインディング機能を使用することにより、クラスのメンバ変数とデータベースのフィールドを直接マッピングすることができる。<br> <br> トランザクション管理においては、CDatabaseクラスはBeginTransメソッド、Commitメソッド、Rollbackメソッドを提供しており、これらを使用することによりトランザクション制御が可能である。<br> 複数の更新操作を1つの論理的な単位として扱う場合に有効である。<br> <br> エラーハンドリングでは、データベース操作時のエラーは<code>CDBException</code>クラスとして捕捉できる。<br> <br> セキュリティ対策として、パラメータ化クエリを使用することにより、SQLインジェクション攻略のリスクを軽減することができる。<br> MFCでは、<code>CCommand</code>クラスを使用してパラメータ化クエリを実装できる。<br> <br> マルチスレッド環境でデータベースアクセスを行う場合は、適切な同期機構を実装する必要がある。<br> 各スレッドで個別のデータベース接続を使用することが推奨される。<br> <br> また、アプリケーションの要件に応じて、キャッシング機構の導入や接続プーリングの実装等も検討するとよい。<br> <br> ODBCを使用したデータベースの更新手順は、以下に示す2つの項目からなる。<br> # ODBCアドミニストレータでのデータソース登録 # データベースの操作 <br><br> == ODBCアドミニストレータでのデータソース登録 == データソースを登録することにより、アプリケーションからDSN名を使用してデータベースに接続することができる。<br> <br> アプリケーションがx86向けの場合はx86向けのODBC、アプリケーションがAMD64の場合はAMD64向けのODBCを使用する。<br> <br> <u>※注意</u><br> <u>必要なデータベースドライバが事前にインストールされている必要がある。</u><br> <u>ネットワーク接続の場合、ファイアウォールのポート開放が必要となる。</u><br> <br> ==== ODBCデータソースアドミニストレーターの起動方法 ==== * [コントロールパネル] - [Winodwsツール] または [管理ツール] - [ODBC データ ソース (64ビット)] または [ODBC データ ソース (32ビット)]を選択する。 * [Ctrl] - [R]キーを同時押下して、<u>"ODBC"</u>と入力・検索する。 * 64ビットアプリケーションの場合は、C:\Windows\SysWOW64\odbcad32.exeを実行する。 <br> ==== システムDSNの追加手順 ==== # [システムDSN]タブを選択する。 # [追加]ボタンを押下する。 # 使用するデータベースドライバを選択する。 #* SQL Serverの場合 #*: [SQL Server] または [ODBC Driver XX for SQL Server] #* Oracleの場合 #*: [Oracle in XXXXX] #* MySQLの場合 #*: [MySQL ODBC X.X Driver] # [完了]ボタンを選択する。 <br> ==== DSNの設定例 ==== ===== SQL Server用のDSN設定例 ===== # DSNの設定を行う。 #* 名前 #*: アプリケーションで使用するDSN名を入力 (例 : SQLServerTest) #* 説明 #*: 任意の説明を入力 #* サーバー #*: SQLServerのインスタンス名を選択 #* 認証方法の選択 #*: Windows認証 #*: SQLServer認証 (ユーザIDとパスワードが必要) #* [デフォルトデータベースの変更]から、接続するデータベースを選択する。 # [次へ]ボタンを押下する。 # 必要に応じて追加オプションを設定する。 # [完了]ボタンを押下する。 # [テストデータソース]で接続確認を行う。 <br> ===== Oracle用のDSN設定例 ===== # DSNの設定を行う。 #* [データソース名] #*: DSN名を入力する。 #* [説明] #*: 任意の説明を入力する。 #* [TNSサービス名] #*: 接続するOracleサービスを選択する。 #* [UserID] #*: デフォルトのユーザID (オプション) # [OK]ボタンを押下する。 <br> ===== MySQL用のDSN設定例 ===== # DSNの設定を行う。 #* [Data Source Name] #*: DSN名を入力する。 #* [TCP/IP Server] #*: データベースサーバのホスト名またはIPアドレスを入力する。 #* [Port] #*: MySQLのポート番号を入力する。(デフォルト3306番) #* [User] #*: データベースユーザ名を入力する。 #* [Password] #*: パスワードを入力する。 #* [Database] #*: 接続するデータベース名する。 # [Test]ボタンを押下して、接続テストを行う。 # [OK]ボタンを押下する。 <br> ==== エラー関連 ==== ===== ドライバが表示されない場合 ===== 適切なドライバをインストールする。<br> <br> ===== 接続テストに失敗する場合 ===== ネットワーク設定および認証情報を確認する。<br> <br> ===== データベースが表示されない場合 ===== アクセス権限を確認する。<br> <br><br> == 接続の確立 == データベース接続を開始するには、CDatabaseクラスのインスタンスを生成して、OpenメソッドまたはOpenExメソッドを実行する。<br> 接続文字列には、DSN、ユーザ名、パスワード等の認証情報を含めることができる。<br> <br> <syntaxhighlight lang="c++"> // 接続文字列の制定 CString strConnect = _T("DSN=MyDataSource;UID=user;PWD=password;"); // データベースの接続 CDatabase database; database.OpenEx(strConnect, CDatabase::noOdbcDialog); </syntaxhighlight> <br><br> == エラーハンドリング == データベースの接続エラー、クエリ実行エラー、その他の予期せぬエラー等に対応することが重要である。<br> <br> <syntaxhighlight lang="c++"> CString strConnect = _T("DSN=TestDB;"); CDatabase db; CRecordset rs(&db); try { // データベース接続 if (!db.OpenEx(strConnect, CDatabase::noOdbcDialog)) { AfxMessageBox(_T("データベース接続に失敗")); return; } // クエリ実行 rs.Open(CRecordset::forwardOnly, _T("SELECT * FROM TestTable")); // データ処理 while (!rs.IsEOF()) { // データ処理コード rs.MoveNext(); } } catch (CDBException *e) { CString strError; strError.Format(_T("データベースエラー: %s"), e->m_strError); AfxMessageBox(strError); e->Delete(); } catch (...) { AfxMessageBox(_T("予期せぬエラーが発生")); } </syntaxhighlight> <br><br> == レコードの抽出 == <syntaxhighlight lang="c++"> #include <afxdb.h> void CSampleDlg::OnBnClickedQueryButton1() { // 接続文字列生成 CString strCon = "DSN=MS Access 97 Database;UID=Admin;PWD="; // Windows認証でSQLServerを使用する場合 => CString strCon = "DSN = SQLServerTest;"; // 接続 CDatabase db; db.OpenEx( _T( strCon ), CDatabase::noOdbcDialog ); // db.OpenEx( _T( strCon ), CDatabase::openReadOnly | CDatabase::noOdbcDialog ); // 読み込み専用 // CDatabase::openReadOnly データソースを読み取り専用で開く // CDatabase::noOdbcDialog 必要な情報が提供されているかに関わらず、ODBC接続のダイアログボックスを表示しない // CDatabase::forceOdbcDialog ODBC接続のダイアログ ボックスを常に表示する // 実行 CRecordset rs(&db); try { // SQLコマンドの実行 rs.Open( CRecordset::forwardOnly, _T("select * from test") ); CODBCFieldInfo fi; short nFields = rs.GetODBCFieldCount(); //フィールド名を表示 if (!rs.IsEOF()) { for(short index = 0; index < rs.GetODBCFieldCount(); index++) { rs.GetODBCFieldInfo(index, fi); TRACE("%s\n",fi.m_strName); } } // 値を表示 while (!rs.IsEOF()) { for(short index = 0; index < rs.GetODBCFieldCount(); index++) { CString strValue; rs.GetFieldValue(index, _T(strValue)); TRACE("%s\n", strValue); } rs.MoveNext(); // 次のレコードに移動 } } catch (...) { // SQLがエラーを発生した場合はここで捕捉 // メモリリークのように見えるがODBCドライバが自動的に解放する } rs.Close(); db.Close(); } </syntaxhighlight> <br><br> == レコードの挿入 == ==== 基本的な挿入 ==== <syntaxhighlight lang="c++"> #include <afxdb.h> // データベース接続文字列の設定 // MS Access 97データベースに接続する場合の接続文字列 CString strCon = "DSN=MS Access 97 Database;UID=Admin;PWD="; // 注意 : Windows認証でSQLServerを使用する場合は以下の接続文字列を使用 // CString strCon = "DSN = SQLServerTest;"; // データベースオブジェクトの作成と接続 CDatabase db; db.OpenEx(_T(strCon), CDatabase::noOdbcDialog); // データベース接続オプションの説明: // CDatabase::noOdbcDialog - ODBCの接続ダイアログを表示せずに接続を試みる // CDatabase::forceOdbcDialog - 常にODBCの接続ダイアログを表示する // CDatabase::openReadOnly - 読み取り専用モードでデータベースを開く try { // トランザクションの開始 // データの整合性を保つため、INSERT操作はトランザクション内で実行することを推奨 db.BeginTrans(); // 日付文字列のフォーマット(YYYY-MM-DD HH:MM:SS) COleDateTime currentDateTime = COleDateTime::GetCurrentTime(); CString strDateTime = currentDateTime.Format(_T("%Y-%m-%d %H:%M:%S")); // 数値の金額をフォーマット (小数点以下2桁) double dblAmount = 1234.56; CString strAmount; strAmount.Format(_T("%.2f"), dblAmount); // バイナリデータの準備 (例 : 16進数データ) BYTE binaryData[] = {0x01, 0x02, 0x03, 0x04}; CString strBinary; for (int i = 0; i < sizeof(binaryData); i++) { CString temp; temp.Format(_T("%02X"), binaryData[i]); strBinary += temp; } // INSERT文の実行 // 注意:実際のテーブル構造に合わせてカラム名と値を設定する必要があります CString strSQL; strSQL.Format(_T("INSERT INTO test (column1, column2, column3) VALUES ('%s', %d, '%s')"), _T("サンプル値1"), // 文字列型カラムの例 123, // 数値型カラムの例 _T("サンプル値2")); // 文字列型カラムの例 // SQL文の実行 // ExecuteSQL関数を使用してINSERT文を実行 db.ExecuteSQL(strSQL); // トランザクションのコミット // エラーが発生しなかった場合、変更を確定 db.CommitTrans(); } catch (CDBException *e) { // データベースエラーの処理 // エラーメッセージの取得と表示 TRACE(_T("データベースエラー: %s\n"), e->m_strError); // トランザクションのロールバック // エラーが発生した場合、変更を取り消し db.Rollback(); // 例外オブジェクトの解放 e->Delete(); } catch (...) { // その他の予期しないエラーの処理 TRACE(_T("予期しないエラーが発生しました\n")); // トランザクションのロールバック db.Rollback(); } // データベース接続のクローズ db.Close(); </syntaxhighlight> <br> ==== 各データ型の挿入 ==== <syntaxhighlight lang="c++"> // INSERT文の作成 CString strSQL; strSQL.Format(_T("INSERT INTO SampleTable ") _T("(VarcharColumn, ") // VARCHAR型 _T("NvarcharColumn, ") // NVARCHAR型 _T("IntColumn, ") // INT型 _T("BigIntColumn, ") // BIGINT型 _T("SmallIntColumn, ") // SMALLINT型 _T("TinyIntColumn, ") // TINYINT型 _T("BitColumn, ") // BIT型 _T("DecimalColumn, ") // DECIMAL型 _T("FloatColumn, ") // FLOAT型 _T("DateColumn, ") // DATE型 _T("DateTimeColumn, ") // DATETIME型 _T("DateTime2Column, ") // DATETIME2型 _T("TimeColumn, ") // TIME型 _T("BinaryColumn, ") // BINARY型 _T("UniqueIdentifier) ") // UNIQUEIDENTIFIER型 _T("VALUES ") _T("('%s', ") // VARCHAR _T("N'%s', ") // NVARCHAR(Unicode文字列) _T("%d, ") // INT _T("%I64d, ") // BIGINT _T("%d, ") // SMALLINT _T("%d, ") // TINYINT _T("%d, ") // BIT _T("%s, ") // DECIMAL _T("%f, ") // FLOAT _T("'%s', ") // DATE _T("'%s', ") // DATETIME _T("'%s', ") // DATETIME2 _T("'%s', ") // TIME _T("0x%s, ") // BINARY _T("'%s')"), // UNIQUEIDENTIFIER // 各データ型の値を指定 _T("テスト文字列"), // VARCHAR値 _T("Unicode文字列"), // NVARCHAR値 12345, // INT値 9223372036854775807LL, // BIGINT値 (最大値) 32767, // SMALLINT値 255, // TINYINT値 1, // BIT値 (0または1) strAmount, // DECIMAL値 123.456789, // FLOAT値 currentDateTime.Format(_T("%Y-%m-%d")), // DATE値 strDateTime, // DATETIME値 strDateTime, // DATETIME2値 currentDateTime.Format(_T("%H:%M:%S.0000000")), // TIME値 strBinary, // BINARY値 _T("12345678-1234-1234-1234-123456789012") // UNIQUEIDENTIFIER値 ); </syntaxhighlight> <br> ==== NULL値を含む挿入 ==== <syntaxhighlight lang="c++"> CString strSQLWithNull; strSQLWithNull.Format(_T("INSERT INTO SampleTable ") _T("(VarcharColumn, IntColumn) ") _T("VALUES ") _T("(NULL, NULL)")); </syntaxhighlight> <br><br> == トランザクション == ロールバック処理自体も失敗する可能性があるため、2重のtry-catch構造を使用することにより、ロールバック処理中のエラーも適切に処理できる。<br> <br> <syntaxhighlight lang="c++"> // 接続文字列の生成 CString strCon = _T("DSN=MS Access 97 Database;UID=Admin;PWD="); CDatabase db; bool bTransStarted = false; // トランザクションの状態を追跡するフラグ try { // 接続 if (!db.OpenEx(strCon, CDatabase::noOdbcDialog)) { AfxMessageBox(_T("データベース接続に失敗しました")); return -1; } // トランザクション開始 db.BeginTrans(); bTransStarted = true; // SQLコマンドの実行 db.ExecuteSQL(_T("create table test(a text, b text)")); // db.ExecuteSQL(_T("insert into test(a,b) values('inaba','minoru')")); // db.ExecuteSQL(_T("delete from test where a='inaba'")); // db.ExecuteSQL(_T("DROP TABLE test")); // トランザクションのコミット db.CommitTrans(); bTransStarted = false; } catch (CDBException *e) { // データベース固有のエラー処理 if (bTransStarted) { try { db.Rollback(); } catch (...) { AfxMessageBox(_T("ロールバック中にエラーが発生しました")); } } CString strError; strError.Format(_T("データベースエラー: %s"), e->m_strError); AfxMessageBox(strError); e->Delete(); } catch (...) { // その他の予期せぬエラー処理 if (bTransStarted) { try { db.Rollback(); } catch (...) { AfxMessageBox(_T("ロールバック中にエラーが発生しました")); } } AfxMessageBox(_T("予期せぬエラーが発生しました")); } // データベースを閉じる if (db.IsOpen()) db.Close(); </syntaxhighlight> <br><br> == 注意点 == SQL Server等でSELECT文を発行すると、datetime型カラム等が表示できない場合がある。<br> そのため、GetFieldValueメソッドの引数にCDBVariantを使用する。<br> <br> <center>表. CDBVariantのメンバ</center> <center> {| class="wikitable" |- ! メンバ変数名 !! データ型 |- | m_boolVal || BOOL |- | m_chVal || unsigned char |- | m_dblVal || double |- | m_dwType || DWORD(現在格納されている値のデータ型) |- | m_fltVal || float |- | m_iVal || short |- | m_lVal || long |- | m_pbinary || CLongBinaryオブジェクトへのポインタ |- | m_pdate || TIMESTAMP_STRUCTオブジェクトへのポインタ |- | m_pstring || CStringオブジェクトへのポインタ |} </center> <br> 上表のCDBVariant::m_dwTypeでは、CDBVariantオブジェクトの共用体データメンバに現在格納されている値のデータ型が入る。<br> 共用体にアクセスする前に、共用体のどのメンバにアクセスするかを決めるために、m_dwTypeの値をチェックする必要がある。<br> <br> 下表は、m_dwTypeの値と対応する共用体メンバの一覧である。<br> <br> <center>表. m_dwType共用体メンバ</center> <center> {| class="wikitable" |- ! 共用体データ型 !! 共用体データメンバ |- | DBVT_NULL || 有効な共用体メンバはなく、アクセスできません。 |- | DBVT_BOOL || m_boolVal |- | DBVT_UCHAR || m_chVal |- | DBVT_SHORT || m_iVal |- | DBVT_LONG || m_lVal |- | DBVT_SINGLE || m_fltVal |- | DBVT_DOUBLE || m_dblVal |- | DBVT_DATE || m_pdate |- | DBVT_STRING || m_pstring |- | DBVT_BINARY || m_pbinary |} </center> <br> <syntaxhighlight lang="cpp"> CString strCon = "DSN = SQLServerTest;"; CDatabase db; db.OpenEx(_T( strCon ), CDatabase::noOdbcDialog); CRecordset rs( &db ); try { rs.Open( CRecordset::forwardOnly, _T("select * from test") ); // SQLコマンドの実行 // 値を表示 while (!rs.IsEOF()) { for(short index = 0; index < rs.GetODBCFieldCount(); index++) { CDBVariant val; rs.GetFieldValue(index, val); switch(val.m_dwType) { case DBVT_NULL : TRACE("NULL 無効の値\n"); break; case DBVT_BOOL : TRACE("m_boolVal BOOL 型\n"); TRACE("%d\n",val.m_boolVal); break; case DBVT_UCHAR : TRACE("m_chVal unsigned char 型\n"); TRACE("%s\n",val.m_chVal); break; case DBVT_SHORT : TRACE("m_iVal short 型\n"); TRACE("%d\n",val.m_iVal); break; case DBVT_LONG : TRACE("m_lVal long 型\n"); TRACE("%d\n",val.m_lVal); break; case DBVT_SINGLE : TRACE("m_fltVal float 型\n"); TRACE("%f\n",val.m_fltVal); break; case DBVT_DOUBLE : TRACE("m_dblVal double 型\n"); TRACE("%f\n",val.m_dblVal); break; case DBVT_DATE : TRACE("m_pdate TIMESTAMP_STRUCT 型のオブジェクトへのポインタ\n"); // NULLチェック if (val.m_pdate != nullptr) { TIMESTAMP_STRUCT *pT = val.m_pdate; CString strDateTime; strDateTime.Format(_T("%04d/%02d/%02d %02d:%02d:%02d"), pT->year, pT->month, pT->day, pT->hour, pT->minute, pT->second); TRACE(_T("Field: %s, Value: %s\n"), fieldName, strDateTime); } break; case DBVT_STRING : if (val.m_pstring != nullptr) { TRACE("m_pstring CString 型のオブジェクトへのポインタ\n"); TRACE(_T("Field: %s, Value: %s\n"), fieldName, *val.m_pstring); } break; case DBVT_BINARY : TRACE("m_pbinary\n CLongBinary 型のオブジェクトへのポインタ"); break; default : break; } } rs.MoveNext(); } } catch (CDBException *e) { TRACE(_T("Database error: %s\n"), e->m_strError); e->Delete(); } catch (...) { TRACE(_T("不明なデータベースエラーが発生\n")); } rs.Close(); db.Close(); </syntaxhighlight> <br><br> __FORCETOC__ [[カテゴリ:MFC]]
MFCとデータベース - CRUDの実行
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse