2

Glassfish 3.1.2.2 で実行されている Hibernate (3) と MySQL (5.5) で奇妙な問題が発生しています。単一のサーバー呼び出しのコンテキスト内で実行時に自動コミット フラグの状態が変化しています。同じスレッド。私はそれを明示的に変更しているわけではなく、最近突然始まりました。この領域のコードは大幅に変更されていません。

私が使用しているコードにはさまざまなデータ アクセス方法があり、それぞれが同じパターンに従います。

  1. Hibernate セッション ファクトリから現在のセッションを取得する
  2. 取引開始
  3. アップデートを実行
  4. トランザクションをコミットする
  5. ステップ 3 と 4 は try/catch にあり、例外が発生した場合、トランザクションはロールバックされます。

このようなメソッド呼び出しの最初の 2 回では、自動更新フラグは false ( getDefaultSession().connection().getAutoCommit()) ですが、3 回目以降はすべて、フラグが突然 true になり、どのように/なぜ変更されたかは示されません。フラグが true の場合にコミットをスキップするために、いくつかのメソッドに回避策のコードを追加する必要がありましたが、これは最近発生し始めたばかりの問題であり、すべてのメソッドを更新することはできません。

また、そのスレッド ID が次回セッションを取得したときに、状態が true として開始されるため、自動コミット フラグが現在のセッションで確実に true として保存されることも問題です。そのスレッドで次に使用されるデータ アクセス メソッドに回避策のコードがない場合、例外がスローされ、状態が false に戻ります。繰り返しますが、明示的なコードはありません。

いえ

  1. スレッド 1、方法 1
    1. DAO 1: 自動コミット false
    2. DAO 2: 自動コミット false
    3. DAO 3: 自動コミット true
    4. DAO ...n: 自動コミット true
  2. スレッド !1、メソッドの自動コミット false
  3. スレッド 1、メソッド
    1. DAO x: 自動コミット true
    2. 例外
    3. DAO y: 自動コミット false

Hibernate 構成で自動コミットを有効/無効にする方法について多くのスレッドを見つけましたが、次の構成項目があります。

<property name="hibernate.connection.autocommit">false</property>

複数のスレッドを使用するとフラグが true に変更される可能性があることを示すスレッドもいくつか見つかりましたが、これらのメソッドはすべて (変更が発生した前後で) 同じ初期メソッドから同じスレッド内で呼び出されています (ThreadID に基づいて確認) Glassfish ログで)。

JDBC URL のリラックス自動コミット フラグについて読みましたが、そもそもなぜそれが起こっているのかを突き止めたいと思います。最後の手段として、すべてのコードを変更する必要がないように、そうする必要があるかもしれません。

まじで混乱…

4

1 に答える 1

0

Glassfish 4.0 でも同じ問題が発生しており、その根底にも到達していません。ほとんどの人は、relaxAutoCommitフラグを設定して忘れているようです。私たちが行ったテストでsetAutoCommit(false)は、返された状態がgetAutoCommit()正しくないだけでなく、実際に MySQL データベースにクエリを実行SELECT @@session.autocommitすると、セッションに対して自動コミットがまだ有効になっていることがわかります。

設定relaxAutoCommitは危険なので避けるべきだと思います!Connector/Jのドキュメントを実際に読んでみるとrelaxAutoCommit、明確な目的があることがわかります。

ドライバーが接続する MySQL のバージョンがトランザクションをサポートしていない場合でも、commit()、rollback()、および setAutoCommit() の呼び出しを許可しますか (true/false、デフォルトは「false」)?

例外を抑制するためだけに設定するのは間違いです、IMO。

最終的に を実装したcom.mysql.jdbc.ConnectionLifecycleInterceptorところ、何らかの理由でsetAutoCommit(false)、プールから返された接続で呼び出しを行うと、ドライバーの接続で実際に自動コミットを設定できず、セッションでの設定に失敗することがあることがわかりました。

接続がこの一貫性のない状態になったときに操作を再試行することで、この問題を回避しました。

ConnectionLifecycleInterceptor と追加のログを有効にすると、アプリケーションのログで生成される出力の一部が次のようになります。

18 Feb 16:47:07 ERROR [http-listener-1(11)] - auto-commit set to: false
18 Feb 16:47:07  INFO [http-listener-1(11)] - Autocommit before command: SetActionCommand
18 Feb 16:47:07  INFO [http-listener-1(11)] - Autocommit state is: connAutocommit=true, sessAutocommit=true
18 Feb 16:47:07  INFO [http-listener-1(11)] - SetActionCommand: host: bat424211win64, action: Checking, comment: Checking:20160218.164706.EST: Stuck Windows Check
18 Feb 16:47:07  INFO [http-listener-1(11)] - Autocommit after command: SetActionCommand
18 Feb 16:47:07  INFO [http-listener-1(11)] - Autocommit state is: connAutocommit=true, sessAutocommit=true
18 Feb 16:47:07 ERROR [http-listener-1(11)] - commit called
18 Feb 16:47:07 ERROR [http-listener-1(11)] - auto-commit should be false
18 Feb 16:47:07 ERROR [http-listener-1(11)] - Exception while executing: SetActionCommand
18 Feb 16:47:07 ERROR [http-listener-1(11)] - rollback called
18 Feb 16:47:07 ERROR [http-listener-1(11)] - auto-commit should be false
18 Feb 16:47:07  WARN [http-listener-1(11)] - Connection failure
18 Feb 16:47:07 ERROR [http-listener-1(11)] - 

ConnectionLifecycleInterceptor は、setAutoCommit(false) を呼び出しているという事実をログに記録し、SetActionCommand の呼び出しの前後に追加のログを記録して、getAutoCommit() と session.autocommit の結果を報告しています。

関連する Connector/J のビットと、私自身の追加コメントの一部:

com.mysql.jdbc.ConnectionImpl:

public void setAutoCommit(final boolean autoCommitFlag) throws SQLException {
    synchronized (getConnectionMutex()) { // Mutex is 'this'
        checkClosed();

        if (this.connectionLifecycleInterceptors != null) { // Normally should be 'null'
            IterateBlock<Extension> iter = new IterateBlock<Extension>(this.connectionLifecycleInterceptors.iterator()) {

                @Override
                void forEach(Extension each) throws SQLException {
                    if (!((ConnectionLifecycleInterceptor) each).setAutoCommit(autoCommitFlag)) { // Logged 'auto-commit set to: false'
                        this.stopIterating = true;
                    }
                }
            };

            iter.doForAll();

            if (!iter.fullIteration()) { // Only one listener (ours) AFAIK, unlikely to return here
                return;
            }
        }

        if (getAutoReconnectForPools()) { // Default for autoReconnectForPools is 'false'
            setHighAvailability(true);
        }

        try {
            if (this.transactionsSupported) { // This is 'true'

                boolean needsSetOnServer = true;

                if (this.getUseLocalSessionState() && this.autoCommit == autoCommitFlag) { // Default for useLocalSessionState is 'false'
                    needsSetOnServer = false;
                } else if (!this.getHighAvailability()) { // This is 'false'
                    needsSetOnServer = this.getIO().isSetNeededForAutoCommitMode(autoCommitFlag); // Looks like this is always 'true'?
                }

                // this internal value must be set first as failover depends on it being set to true to fail over (which is done by most app servers and
                // connection pools at the end of a transaction), and the driver issues an implicit set based on this value when it (re)-connects to a
                // server so the value holds across connections
                this.autoCommit = autoCommitFlag; // Updated value is not reflected in getAutoCommit()!  We never get here?

                if (needsSetOnServer) {
                    execSQL(null, autoCommitFlag ? "SET autocommit=1" : "SET autocommit=0", -1, null, DEFAULT_RESULT_SET_TYPE,
                            DEFAULT_RESULT_SET_CONCURRENCY, false, this.database, null, false);
                }

            } else {
                if ((autoCommitFlag == false) && !getRelaxAutoCommit()) {
                    throw SQLError.createSQLException("MySQL Versions Older than 3.23.15 do not support transactions",
                            SQLError.SQL_STATE_CONNECTION_NOT_OPEN, getExceptionInterceptor());
                }

                this.autoCommit = autoCommitFlag;
            }
        } finally {
            if (this.getAutoReconnectForPools()) {
                setHighAvailability(false);
            }
        }

        return;
    }
}
com.mysql.jdbc.MysqlIO:
    protected boolean isSetNeededForAutoCommitMode(boolean autoCommitFlag) {
    if (this.use41Extensions && this.connection.getElideSetAutoCommits()) { // Default for elideSetAutoCommits is 'false'
        boolean autoCommitModeOnServer = ((this.serverStatus & SERVER_STATUS_AUTOCOMMIT) != 0);

        if (!autoCommitFlag && versionMeetsMinimum(5, 0, 0)) {
            // Just to be safe, check if a transaction is in progress on the server....
            // if so, then we must be in autoCommit == false
            // therefore return the opposite of transaction status
            boolean inTransactionOnServer = ((this.serverStatus & SERVER_STATUS_IN_TRANS) != 0);

            return !inTransactionOnServer;
        }

        return autoCommitModeOnServer != autoCommitFlag;
    }

    return true; // Should always be returning 'true'
}

何かが iter.fullIteration() に false を返させない限り、実際に何かをする前に setAutoCommit() が戻っているように見える理由について、私は困惑しています。

于 2016-02-18T17:58:08.957 に答える