QtはIoT管理プラットフォーム38を作成します-複数のデータベースのサポート

I.はじめに

このシステムの設計当初は、sqlite、mysql、postgres、sqlserverなどのさまざまなデータベース、さらにはNPC Jincangkingbaseなどの国内データベースもサポートする必要がありました(ローカリゼーションの積極的な推進、国内データベースもサポートする必要があります)、Qtにカプセル化されたデータベースコンポーネントsqlモジュールには、すべてのデータベースの抽象化レイヤーがあり、アプリケーションが複数のデータベースをサポートするための良い前提となるため、次のような詳細が処理されますデータベースの日付範囲クエリとして、データベースごとに処理方法が異なり、カプセル化された独自のコンポーネントでの処理とデータベースのページングが異なります。データベースごとのページングクエリステートメントは異なります。たとえば、sqliteとmysqlは制限キーを使用します。Word、 postgresおよびkongbaseデータベースはlimitoffsetキーワードを使用し、sqlserverはtopキーワードを使用し、oracleは最も複雑であり、非表示の列と組み合わせる必要があります。

データベースから学んだいくつかの教訓:

  • sqliteデータベースにはデータ型の概念はなく、設定したintは引き続き文字列を格納できます。他のデータベースにはデータ型の違いがあります。
  • データベースフィールドを設計するときは、データベースキーワードや、年、月、計画などの関数キーワードを使用しないようにしてください。必要に応じて、SQLステートメントを実行するときに対応するフィールドに引用符を追加します。
  • sqliteデータベースに加えて、他のデータベースには2の設定などの長さの要件があり、3の長さを挿入し続けると、エラーが報告され、実行が失敗します。
  • varcharタイプは文字列を格納し、gbkは次の漢字を2バイトでエンコードし、utf8は次の漢字を3バイトでエンコードするため、コンテンツ「enable」などをよく考慮する必要があります。長さを次のように設定するのが最適です。 6バイト。
  • データベースファイルのサイズをできるだけ小さくするために、フィールドの長さがわかっている場合は、既知の長さをできるだけ設定することをお勧めします。
  • 多数のレコードを含むデータベーステーブル、および頻繁にクエリが必要なテーブルには、インデックスを作成する必要があります。そうしないと、非常に遅くなります。
  • 数千エントリ未満など、データ量が少なく、変更がほとんど必要ないテーブルの場合、インデックスを設定する必要がないため、挿入速度が速くなります。
  • 整数フィールドINTおよびINTEGERの長さは設定できず、長さを設定する必要はありません。そうしないと、sqlserverなどのデータベースの実行時にエラーが報告されます。
  • テーブルごとに主キーフィールドを設定することをお勧めします。特に、インデックスを作成する必要があるテーブルには、主キーフィールドが必要です。
  • Linuxにmysqlclientコマンドをインストールします:apt-getinstalllibmysqlclient-dev。

Qtデー​​タベース開発に関するいくつかの冷たい知識。

  • Qtは、ライブラリ形式のデータベースとの直接通信をサポートし、すべての状況をカバーするODBCデータソース形式のさまざまなデータベースとの通信もサポートします。
  • Qtデー​​タベースプログラムはパッケージ化されてリリースされています。すべての前提条件:32ビットと64ビットを区別するように注意してください。プログラムが32ビットの場合は、32ビットライブラリを用意する必要があります。64ビットは64ビットライブラリを用意する必要があります。 Qtライブラリも必要です。mysqlをリリースする最も簡単な方法は、mysqlダイナミックライブラリファイル(Windowsではlibmysql.dll)を用意することです。これは非常に簡単です。sqlserverはMicrosoftの息子であり、一般的なオペレーティングシステムに付属しているため、持ってくる必要はありません。Postgresは、libpq.dll、libintl-8.dll、libiconv-2.dll、libeay32.dll、ssleay32.dllこれらのファイルを持ってくる必要があります。Oracleはoci.dllとoraociei11.dllを持ってくる必要があります(このファイルは非常に大きく、130MB以上あります)。そうでない場合は、oracleクライアントクライアントソフトウェアを直接インストールしてから、対応するbinディレクトリを環境変数に設定することをお勧めします。
  • パッケージがリリースされてテストされた後、32ビットプログラムも64ビットmysqlに正常に接続でき、64ビットプログラムも32ビットmysqlに正常に接続できることがわかりました。したがって、それを判断するだけで済みます。プログラムのライブラリ内のビット数は同じです(コンパイル時)。これもルールです。32ビットQtプログラムコンパイルデータベースプラグインも32ビットデータベースリンクライブラリを使用します。)、である必要はありません。特定のデータベースの数と一致して、テストされたmysql、sqlserver、postgresqlデータベースはすべて同様のルールです。
  • 多数のテストを比較し、odbcデータソース方式と直接接続方式で多数のデータレコードをまとめて挿入します。直接接続方式の方が5%程度高速なので、この方式の使用をお勧めします。可能な限り。odbcデータソース方式を使用すると、Qtにはデフォルトでodbcデータベースプラグインが付属しています。
  • 異なるデータベースは、sqlスクリプトの実行時にテーブル名またはフィールド名を大文字または小文字に自動的に変換し、mysqlはテーブル名を小文字に変換し、postgresqlはテーブル名とフィールド名を小文字に変換し、oracleはテーブル名とフィールドを変換しますname大文字に変換します。これにより、QSqlTableModelを使用してsetTableを呼び出してデータベーステーブル名を設定する場合、データベース内のテーブル名と同じであり、大文字と小文字が区別されるため、postgresqlデータベースとoracleデータベースに注意する必要があります。長い間ここで立ち往生しています。Qtのバグからこの巨大なたわごとを差し引きたいと思っています。
void DbHelper::bindTable(const QString &dbType, QSqlTableModel *model, const QString &table)
{
    
    
    //postgresql全部小写,oracle全部大写,这两个数据库严格区分表名字段名的大小写卧槽
    QString flag = dbType.toUpper();
    if (flag == "POSTGRESQL") {
    
    
        model->setTable(table.toLower());
    } else if (flag == "ORACLE") {
    
    
        model->setTable(table.toUpper());
    } else {
    
    
        model->setTable(table);
    }
}
  • Qtは、データベースサーバーに接続した後にデータベースを作成するためにsqlステートメントを実行する必要がある場合があるため、データベース名を指定せずにデータベースを開くことをサポートしています。データベースに接続する方法はまだ存在していません。テストでは、sqlite、mysql、sqlserver、postgresqlがすべてこの機能をサポートしていることがわかりました。データベースの削除と作成の前提は、データベースが他のプログラムによって占有されていないことです。たとえば、他のプログラムがすでにデータベースを開いている場合、実行は失敗します。私はここで何度も拷問を受けましたが、なぜ処刑が失敗したのですか?その後、サードパーティのデータベースツールがデータベースを開いたことが判明し、ツールをオフにしても問題ありませんでした。
QSqlDatabase database = QSqlDatabase::addDatabase("QMYSQL");
//database.setDatabaseName("dbtool");
database.setHostName("127.0.0.1");
database.setPort(3306);
database.setUserName("root");
database.setPassword("root");

if (database.open()) {
    
    
    QSqlQuery query(database);
    qDebug() << "删除数据库" << query.exec("drop database dbtool");
    qDebug() << "创建数据库" << query.exec("create database dbtool");
    if (query.exec("select * from userinfo")) {
    
    
        while (query.next()) {
    
    
            qDebug() << "查询数据库" << query.value(0);
        }
    }
} else {
    
    
     qDebug() << "打开数据库" << database.lastError().text();
}
  • QSqlQueryModel + QTableViewを使用してデータを表示します。int型のデータが100万を超えると、科学的記数法の表示になり、非常に煩わしくなり、希望する結果にはなりません。インターネット全体を検索した後、私はついに同じ問題を抱えている仲間を見つけました。この列に空の手数料を追加する必要があります。その後、空のコミッションでは不十分であることが判明しました。1,000万を超えるアイテムは不良であり、データモデルは究極のDafaによってリロードする必要があります。
ui->tableView->setItemDelegateForColumn(0, new QItemDelegate);

//下面是终极大法
QVariant SqlQueryModel::data(const QModelIndex &index, int role) const
{
    
    
    QVariant value = QSqlQueryModel::data(index, role);
    //超过100万的数值会被科学计数显示需要这里转成字符串显示
    if (role == Qt::DisplayRole) {
    
    
        int result = value.toInt();
        if (result >= 1000000) {
    
    
            value = QString::number(result);
        }
    }
    return value
}
  • mysqlデータベースにはさまざまなデータベースエンジンがあり、その中でMyIsamはデータベーストランザクションをサポートしていません。デフォルトは通常このエンジンであるため、Qtでトランザクションメソッドを使用した後にコミットすると、失敗することがわかります。実際、成功しています。 。データベースに移動します。内部の対応する結果を確認してください。正しいです。2つの方法があります。1つはデータベースエンジンをInnoDBに変更する方法、もう1つは(database.commit()||!database.lastError()。isValid())、エラーを送信した後にエラー判断を行う方法です。説明は成功しました。
  • odbcデータソース通信を使用する場合、データソース構成によって対応するホストアドレスとポート、および関連するデータベースがすでに設定されているため、データベース名setDatabaseName、ユーザー名setUserName、ユーザーパスワードsetPasswordを設定するだけで済みます。名前なので、odbcデータソースと通信するときにユーザー情報を再度確認するだけで済みます。ここで特に注意するのは、setDatabaseNameがデータベース名を設定してデータソース構成の名前を入力することです。
  • Qtデー​​タベースの応答速度で、数千万のデータを使用した挿入、削除、バッチ処理、クエリ、ページングなどの操作を含む多くの比較テストの後、親しみやすさのランキングはsqlite>postgresql>oracleです。 >mysql>odbc。数千万以上はpostgresql>oracle>mysql>sqlite>odbcです。10億レベルを超えるのは、oracle>postgresql>その他です。上記のテストは初心者レベルに基づいており、サブデータベースやサブテーブル、共同クエリ、キャッシュ、メモリデータベースなどのさまざまな高度な知識ポイントは役に立ちません。
  • mysqlには、mysql5.7とmysql8の2つの主要なバージョンがあります。公式には、8は5よりもはるかに高速です。個人テスト後、5.7は8よりもはるかに高速です。データのクエリまたはバッチでの挿入のどちらであるかはわかりません。 、しかし私はオンラインで検索しました。これは結果でもあり(https://www.coder4.com/archives/7596)、誰もが8ははるかに遅いと言いました。
  • mysqlよりも純粋なmariadbと呼ばれるmysqlのブランチがあります。mysqlはすべての面でハングしていると言われています(https://blog.csdn.net/x275920/article/details/123847792)。テストは、バッチ挿入とクエリのパフォーマンスにも当てはまります。これははるかに優れており、mysqlと完全に互換性があります。ライブラリファイルの名前を直接変更することで、ライブラリファイルを直接使用することもできます。たとえば、libmariadb.dllをlibmysql.dllに変更できます。直接、そしてボリュームは8分の1です。これは良いです、そしてそれはより少ない頻度でリリースされます。数メガバイト。
  • Qt + mysqlプログラムの場合、リリースされたときのライブラリのバージョンは、プラグインに対応するデータベースのバージョンと同じである必要があります。そうでない場合、データベーストランザクション機能とdatabase.driver()がない可能性があります。 -> hasFeature(QSqlDriver :: Transactions)はfalseです。
  • QSqlTableModelは非常によくカプセル化されており、一度にすべてのデータをロードするわけではありませんが、スクロールバーが引かれるときに必要なデータをロードし、数千のテーブルと同じ非常に高速な1億のテーブルをテストします。
  • ネットワークデータベースに接続するときに、ローカルネットワークがプロキシを設定している場合(githubや他のWebサイトでプロキシを使用する場合など)、Qtデータベースプログラムを接続できないことがわかります。QNetworkProxyFactory:: setUseSystemConfiguration(falseを設定する必要があります。ローカルプロキシを使用しない)))。この場所で注意を怠ると、問題が見つかり、人生についての疑問が見つかります。

2.機能

2.1ソフトウェアモジュール

  1. データ監視(テーブル表示)、機器パネル(パネル表示)、マップ監視(マップ表示)、曲線監視(曲線表示)を含む機器監視モジュール。
  2. アラーム記録、操作記録、操作記録を含むデータクエリモジュール。
  3. 基本設定、ポート管理、コントローラー管理、検出器管理、アラームリンケージ、タイプ設定などを含むシステム設定モジュール。
  4. ユーザー管理、マップ管理、場所の調整、構成設計、機器のデバッグなどを含むその他の設定モジュール。

2.2基本機能

  1. デバイスデータ取得、サポートシリアルポート、ネットワーク、シリアルポート番号、ボーレートを設定でき、ネットワークはIPアドレス、通信ポートを設定できます。
  2. 各ポートは、デフォルトで1秒あたり1デバイスの収集サイクル時間をサポートします。
  3. 通信タイムアウト数の設定をサポートします。デフォルトは3回です。
  4. オフラインデバイスを再読み取りするための最大再接続時間をサポートします。
  5. コントローラー情報。コントローラー名を追加したり、コントローラーアドレス、コントローラーモデルを選択したり、コントローラーの下にある検出器の数を設定したりできます。
  6. 検出器情報、タグ番号、検出器モデル、ガスタイプ、ガス記号、高アラーム値、低アラーム値、バッファ値、クリア値、有効にするかどうか、アラーム音、背景マップ、保存期間、数値変換小数点数、アラーム遅延時間、アラームタイプ(HH、LL、HL)など。
  7. タイプ管理では、コントローラーモデル、検出器モデル、ガスタイプ、ガスシンボルなどを構成できます。
  8. マップはインポートと削除をサポートしており、マップ上のすべての検出器の位置を自由にドラッグして保存できます。
  9. ポート情報、コントローラー情報、検出器情報、タイプ情報、ユーザー情報などはすべて、インポート、エクスポート、Excelへのエクスポート、および印刷をサポートしています。
  10. 操作レコード、アラームレコード、および操作レコードはすべて、期間、コントローラー、検出器などの複数条件の組み合わせクエリをサポートします。すべてのレコードは、Excel/PDFへのエクスポートと印刷をサポートします。
  11. 指定した時間範囲内のデータは、運転記録、警報記録、運転記録から削除することができます。
  12. システム設定では、対応するテーブルに保存されるレコードの最大数を選択し、初期データを自動的にクリーンアップし、重要なデータを保存するのに十分なスペースを残すことができます。
  13. アラームSMS転送、複数の受信携帯電話番号をサポートし、6時間ごとの即時送信やすべてのアラーム情報の送信などの送信間隔を設定できます。SMSの内容が長すぎる場合は、複数のSMSメッセージが自動的に分割されます。
  14. アラームメール転送、複数の受信メールボックスをサポートし、6時間に1回の即時送信やすべてのアラーム情報の送信などの送信間隔を設定でき、添付ファイルの送信をサポートします。
  15. ソフトウェアの中国語タイトル、英語タイトル、ロゴパス、著作権などを設定します。
  16. 設定を切り替えて、実行開始、アラーム音、自動ログイン、パスワードの記憶などを行います。
  17. 目覚まし時計は再生時間に設定でき、インターフェーススタイルは18セットのスキンファイルから選択できます。
  18. ユーザー権限の構成を含むユーザー管理では、ユーザーごとに異なるモジュール権限を持つことができます。
  19. ユーザーログインとユーザーログアウト。パスワードを覚えて自動的にログインし、エラーメッセージを3回以上報告して、プログラムを閉じることができます。
  20. デバイスパネル監視、マップ監視、テーブルデータ監視、カーブデータ監視の4つの監視モードを自由に切り替えることができます。4つのモードすべてで収集データがリアルタイムで表示され、アラームが点滅します。
  21. アラームリレーリンケージ、1つのタグ番号で複数のモジュールとリレー番号をシリアルポート全体にリンクでき、多対多をサポートします。

2.3機能

  1. 通信プロトコルはmodbus_com、modbus_tcp_rtuをサポートし、後でmqttなどのプロトコルを拡張します。
  2. 実際のハードウェアデバイスの取得に加えて、データベースからデータソースを選択することもできるため、ユーザーは、フロントエンドによって収集されたデータをデータベースに配置するようにjavaプログラマーなどの他のプログラマーを手配でき、システムは直接データベースから収集します。データベース収集モードは、一般的なシステムとして使用でき、複数人および複数システムのコラボレーションに適しています。
  3. 時間外のデバイスをインテリジェントにスキップし、オンラインデバイスの取得を高速化します。特に、デバイスの数が多い場合に役立ちます。
  4. インテリジェントにスキップされたタイムアウトデバイスの場合、デバイスが再びオンラインであるかどうかを検出するために、設定された再接続時間に1回自動的に収集されます。
  5. 各検出器は、有効にするかどうかを制御できます。有効にしないと、収集されず、インターフェイスに表示されません。これは、実行フェーズ中に一時的にシャットダウンすることと同じです。
  6. 検出器はバッファ値とアラーム遅延時間を設定できます。この値の前後の変動によって発生したアラームはアラームとしてカウントされません。検出器が継続的にアラーム値にあり、アラーム遅延時間を超えた場合にのみ、次のように見なされます。変動によって引き起こされる多くのエラーを回避できる実際のアラーム。新聞。
  7. 検出器は保存期間を設定し、設定時間に応じて実行記録を保存できます。重要度に応じて、重要度の高い設定の保存期間を短くしたり、重要でない設定を大きくしたりできるため、節約できます。大量のストレージスペース。また、重要なデータがタイムリーに保存されることを保証します。
  8. 検出器はリセット値に設定できます。高精度・高感度機器が工場出荷される場合、初期値が0にならない場合があります。初期値を表すリセット値を設定する必要があります。
  9. 検出器は小数点を設定できます。これは、計算後の実際のデータの小数点表示を制御するために使用されます。これは、10、100、および1000で除算することに相当します。このようにして、ほとんどの検出器データを直接制御できます。実際の変換値を制御するための小数点設定によって。値、特別な変換を必要とする値は、通信プロトコルで合意することができます。
  10. 検出器アラームには多くの種類があり、特定の値を超える高いレポートと特定の値を下回る低いレポートを報告するデバイスもあれば、最小値と最大値の範囲内の高いレポート、および最小値を下回る低いレポートを報告するデバイスもあります。最大値は正常です。これにより、さまざまな種類のアラームをカバーするケースバイケースの処理が可能になります。
  11. 元のデータのインポート、エクスポート、および印刷メカニズム、コンポーネントに依存しないクロスプラットフォーム、およびデータの即時エクスポート。
  12. Excelにエクスポートされたレコードは、すべてのバージョンのExcel、wps、およびその他のフォームファイルをサポートし、Excelやその他のソフトウェアに依存しません。
  13. ハイレポートカラー、ローレポートカラー、ノーマルカラー、デフォルト値カラーなどを自由に設定できます。
  14. クラウドデータの同期をサポートし、ローカルで収集されたデータをリアルタイムでクラウドに同期します。
  15. ネットワーク転送とネットワーク受信をサポートします。ネットワーク受信がオンになった後、ソフトウェアは分析のためにudpからデータを受信します。ネットワーク転送は複数のターゲットIPをサポートしているため、ローカル収集ソフトウェアをクライアントに自由に転送でき、収集されたデータをいつでも表示できます。
  16. 最後のユーザーインターフェイスとその他の構成情報を自動的に記憶し、再起動後に自動的に適用します。
  17. アラームが自動的に対応するマップに切り替わり、検出器ボタンが点滅し、テーブルデータが対応する色で表示されます。
  18. 検出器アイコンをダブルクリックすると、対応する検出器の詳細情報がポップアップ表示され、必要に応じてコールバック操作をカスタマイズできます。
  19. データベースは、sqlite、mysql、sqlserver、postgresql、oracle、NPCJincangなどのさまざまなデータベースをサポートします。
  20. ローカルデバイスによって収集されたデータはリアルタイムでクラウドにアップロードされるため、モバイルAPPやWebなどの他の方法で抽出できます。
  21. 組み込みのデバイスシミュレーションツールは、異なるモデルの複数のデバイスのデータシミュレーションをサポートし、デバイスがない場合にデータをテストするためのデータベースデータシミュレーションも備えています。
  22. 標準のmodbusプロトコル、さまざまなコントローラータイプ、検出器タイプ、タイプ、シンボルなどはすべてカスタマイズされており、非常に柔軟で強力です。通信プロトコルのサンプルデータは非常に完全で、さまざまなmodbusプロトコルシステムに共通しており、さまざまなアプリケーションシナリオへのアクセスに適しています。
  23. 同時に、シリアル通信、ネットワーク通信、データベース通信、データインポート、エクスポートと印刷、通信プロトコル分析、インターフェイスUI、グローバルスキニングなどの多くのコンポーネントとナレッジポイントを統合します。これは、初心者から上級者まで非常に適しています。 。
  24. xp、win7、win10、win11、linux、mac、さまざまな国内システム(UOS、winning kylin、galaxy kylinなど)、組み込みLinuxおよびその他のシステムをサポートします。
  25. 完全なコメント、明確なプロジェクト構造、非常に詳細で完全な開発マニュアル、各コードファイルの機能説明に正確、および継続的な反復バージョン。

3.体験アドレス

  1. 国内サイト:https ://gitee.com/feiyangqingyun
  2. 国際サイト:https ://github.com/feiyangqingyun
  3. 個人ホームページ:https ://blog.csdn.net/feiyangqingyun
  4. ホームページを知っている:https ://www.zhihu.com/people/feiyangqingyun
  5. 製品ホームページ:https ://blog.csdn.net/feiyangqingyun/article/details/97565652
  6. オンラインドキュメント:https ://feiyangqingyun.gitee.io/qwidgetdemo/iotsystem/
  7. エクスペリエンスアドレス:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A抽出コード:o05qファイル名:bin_iotsystem.zip。
  8. 記事のナビゲーション:https ://qtchina.blog.csdn.net/article/details/121330922

第四に、効果マップ

ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

5.関連コード

void frmConfigDb::on_btnConnect_clicked()
{
    
    
    {
    
    
        //初始化数据库连接信息结构体数据
        DbInfo dbInfo;
        initDbInfo(dbInfo, connName);

        QString dbType = AppConfig::LocalDbType.toUpper();
        if (dbType == "SQLITE") {
    
    
            dbInfo.dbName = DbHelper::getDbDefaultFile(connFlag);
            if (QFile(dbInfo.dbName).size() <= 4) {
    
    
                QUIHelper::showMessageBoxError("数据库文件不存在!", 5);
                return;
            }
        }

        QSqlDatabase database;
        if (DbHelper::initDatabase(true, dbType, database, dbInfo)) {
    
    
            if (database.open()) {
    
    
                database.close();
                QUIHelper::showMessageBoxInfo("打开数据库成功!", 3);
            } else {
    
    
                QString error = database.lastError().text();
                QUIHelper::showMessageBoxError("打开数据库失败!\n" + error, 3);
            }
        } else {
    
    
            QString error = database.lastError().text();
            QUIHelper::showMessageBoxError("连接数据库失败!\n" + error, 3);
        }
    }

    QSqlDatabase::removeDatabase(connName);
}

void frmConfigDb::on_btnInit_clicked()
{
    
    
    if (QUIHelper::showMessageBoxQuestion("确定要初始化数据库吗? 会全部清空数据并且不可还原!") != QMessageBox::Yes) {
    
    
        return;
    }

    QString sqlName = QString("%1/db/%2.sql").arg(QUIHelper::appPath()).arg(connFlag);
    QFile file(sqlName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    
    
        QUIHelper::showMessageBoxError("数据库脚本文件打开失败!", 3);
        return;
    }

    QElapsedTimer time;
    time.start();
    {
    
    
        //初始化数据库连接信息结构体数据
        DbInfo dbInfo;
        initDbInfo(dbInfo, connName);

        QString dbType = AppConfig::LocalDbType.toUpper();
        if (dbType == "SQLITE") {
    
    
            dbInfo.dbName = DbHelper::getDbDefaultFile(connFlag);
            //如果文件存在则先删除原来的数据库文件,貌似win上不行
            QFile file(dbInfo.dbName);
            if (file.exists()) {
    
    
                bool ok = file.remove();
                if (!ok) {
    
    
                    qDebug() << TIMEMS << "remove error" << dbInfo.dbName;
                }

                //清空所有表
                QStringList tables = QSqlDatabase::database().tables();
                foreach (QString table, tables) {
    
    
                    DbHelper::clearTable(table, dbType);
                    qDebug() << TIMEMS << "clearTable" << table;
                }

                //关闭默认数据库连接
                QSqlDatabase::database().close();
            }
        }

        //初始化数据库连接并打开数据库
        QSqlDatabase database;
        if (!DbHelper::initDatabase(true, dbType, database, dbInfo)) {
    
    
            QString error = database.lastError().text();
            QUIHelper::showMessageBoxError("连接数据库失败!\n" + error, 3);
            return;
        }
        if (!database.open()) {
    
    
            QString error = database.lastError().text();
            QUIHelper::showMessageBoxError("打开数据库失败!\n" + error, 3);
            return;
        }

        QSqlQuery query(QSqlDatabase::database(connName));

        //第一步:删除原有数据库
        QString sql = QString("DROP DATABASE %1;").arg(dbInfo.dbName);
        qDebug() << TIMEMS << "sql:" << sql << "result:" << query.exec(sql);

        //第二步:新建数据库
        sql = QString("CREATE DATABASE %1;").arg(dbInfo.dbName);
        qDebug() << TIMEMS << "sql:" << sql << "result:" << query.exec(sql);

        //第三步:切换到新建的数据库库并执行建表语句
        database.close();
        if (!DbHelper::initDatabase(false, dbType, database, dbInfo)) {
    
    
            QString error = database.lastError().text();
            QUIHelper::showMessageBoxError("连接数据库失败!\n" + error, 3);
            return;
        }
        if (!database.open()) {
    
    
            QString error = database.lastError().text();
            QUIHelper::showMessageBoxError("打开数据库失败!\n" + error, 3);
            return;
        }

        //将执行出错的sql语句输出到文件方便查看
        QString fileName2 = QString("%1/db/error.sql").arg(QUIHelper::appPath());
        QFile file2(fileName2);

        QSqlQuery query2(QSqlDatabase::database(connName));

        sql = "BEGIN;";
        qDebug() << TIMEMS << "sql:" << sql << "result:" << query2.exec(sql);

        while (!file.atEnd()) {
    
    
            sql = QString::fromUtf8(file.readLine());
            sql.replace("\n", "");

            //有些数据库不支持的语句跳过去
            if (DbHelper::existNoSupportSql(sql)) {
    
    
                continue;
            }

            //重新纠正sql语句
            DbHelper::checkSql(dbType, sql);

            if (!query2.exec(sql)) {
    
    
                //打印及输出错误信息
                QString error = query2.lastError().text();
                qDebug() << TIMEMS << "sql:" << sql << error;

                //没打开则先打开
                if (!file2.isOpen()) {
    
    
                    file2.open(QFile::WriteOnly | QFile::Append);
                }

                QString msg = QString("时间[%1]  语句: %2  错误: %3\n").arg(DATETIME).arg(sql).arg(error);
                file2.write(msg.toUtf8());
            }
        }

        sql = "COMMIT;";
        qDebug() << TIMEMS << "sql:" << sql << "result:" << query2.exec(sql);
        database.close();

        //sqlite数据库的话再执行下压缩减少体积
        if (dbType == "SQLITE") {
    
    
            DbHelper::execSql("VACUUM;");
        }
    }

    QSqlDatabase::removeDatabase(connName);
    double ms = time.elapsed();
    QString info = QString("数据库脚本执行成功, 总共用时 %1 秒!\n记得重新启动程序!").arg(QString::number(ms / 1000, 'f', 1));
    QUIHelper::showMessageBoxInfo(info, 3);
}

void frmConfigDb::on_cboxDbType_activated(int)
{
    
    
    //自动切换默认端口号和其他信息
    QString hostPort, userName, userPwd;
    QString dbType = ui->cboxDbType->currentText().toUpper();
    DbHelper::getDbDefaultInfo(dbType, hostPort, userName, userPwd);
    if (!hostPort.isEmpty() && AppConfig::LocalAutoInfo) {
    
    
        QStringList hostInfo = ui->txtHostInfo->text().split(":");
        ui->txtHostInfo->setText(hostInfo.at(0) + ":" + hostPort);
        ui->txtUserName->setText(userName);
        ui->txtUserPwd->setText(userPwd);
    }
}

おすすめ

転載: blog.csdn.net/feiyangqingyun/article/details/125593804
おすすめ