Spring-testトランザクションは自動的にロールバックされます
テスト中に、addUser()メソッドを使用してデータベースにレコードを挿入しました。単体テストに合格したことが判明しましたが、レコードはデータベースに挿入されませんでした。ログを見ると、Spring-testがトランザクションをロールバックしていることがわかりました。
テストデータがデータベースを汚染するのを防ぐために、単体テストにSpring-testを使用すると、トランザクションはデフォルトでロールバックされます。つまり、@ Rollbackはデフォルトでtrueになります。テストデータをロールバックしない場合戻って、@ Rollback(value = false)を設定できます。
@Test
@Transactional
@Rollback(value = false)
public void consumerTest(){
Consumer consumer = new Consumer();
consumer.setUsername("栗子");
consumer.setPassword("!@#");
consumer.setSex(new Byte("0"));
consumer.setPhoneNum("12222544441");
consumer.setEmail("[email protected]");
consumer.setBirth(new Date());
consumer.setIntroduction("");
consumer.setLocation("");
consumer.setAvator("/img/user.jpg");
consumer.setCreateTime(new Date());
consumer.setUpdateTime(new Date());
System.out.println(consumerService.addUser(consumer));
}
MySQLデータベースを使用している場合、自動ロールバックを設定した後、トランザクションがまだロールバックされていないことがわかった場合、MyISAMやMemoryなどの他のデータベースエンジンはトランザクションをサポートしていないため、データベースエンジンがInnodbであるかどうかを確認できます。
@トランザクションの詳細
@Transactionalは、宣言型トランザクション管理プログラミングで使用されるアノテーションです。
1.場所を追加します
1)インターフェイスクラスではなく、インターフェイス実装クラスまたはインターフェイス実装メソッド。
2)アクセス権限:パブリックメソッドのみが機能します。@Transactionalアノテーションは、SpringAOPの性質によって決定されるパブリックメソッドにのみ適用する必要があります。
システム設計:すべてのインターフェース実装クラスではなく、トランザクション管理を必要とするメソッドにタグを配置します。@ Transactionalが構成されているため、読み取り専用インターフェースはトランザクション管理を必要としません。AOPインターセプトとトランザクション処理が必要です。システムパフォーマンスに影響します。
3)間違った使用:
1.インターフェースには2つのメソッドAとBがあります。Aには@Transactionalタグがなく、Bにはあります。上位層はAを介して間接的にBを呼び出し、この時点ではトランザクションは有効になりません。
2.インターフェイスの例外(実行時例外)はキャッチされますが、スローされません。デフォルトの構成では、Springは、スローされた例外がランタイムのチェックされていない例外である場合にのみトランザクションをロールバックします。つまり、スローされた例外がRuntimeExceptionのサブクラスであり(エラーによってトランザクションもロールバックされます)、チェックされた例外がスローされます。 。トランザクションがロールバックすることはありません。@TransactionalrollbackForを介して構成できます。
3.マルチスレッドでのトランザクション管理スレッドはSpringによって管理されないため、スレッドはデフォルトでSpringトランザクションを使用できず、Springが注入されたBeanを取得することもできません。マルチスレッドは、Spring宣言型トランザクションによって管理されるメソッドで有効になり、マルチスレッドのメソッドはトランザクションによって制御されません。@Transactionalを使用するメソッドで、メソッドに複数のスレッドの使用が含まれていて、メソッド内で例外が発生した場合、スレッド内のメソッドを呼び出すトランザクションはロールバックされません。
2.宣言型トランザクション管理の実装:
@Transactional Annotation
@Transactionalは、基本的にトランザクション制御にJDBCトランザクションを使用します
@TransactionalSpringベースの動的プロキシメカニズム
@トランザクション実装の原則:
- トランザクションが開始されると、AOPメカニズムを介して、プロキシ接続オブジェクトが生成され、DataSourceTransactionManagerに関連するDataSourceインスタンスのコンテナに配置されます。次のトランザクション全体で、クライアントコードは接続を使用してデータベースに接続し、すべてのデータベースコマンドを実行する必要があります。[接続を使用せずに実行されたデータベースコマンドは、トランザクションがロールバックされるときにロールバックされません](物理接続接続は、新しいセッションセッションを作成するために論理的に作成されます。DataSourceとTransactionManagerは同じデータソースで構成されます)
- トランザクションの終了時に、手順1で取得したプロキシ接続オブジェクトに対して実行されたデータベースコマンドをロールバックし、プロキシ接続オブジェクトを閉じます。トランザクションが終了した後、ロールバック操作は実行されたSQL操作コマンドに影響を与えません)
3.宣言型トランザクション管理の本質:
トランザクションを開くには2つの方法があります:表示開始トランザクション|開始、コミットによるトランザクションの終了|ロールバック、
データベースの自動コミットの終了autocommit set autocommit = 0; MySQLはデフォルトで自動コミットをオンにします;手動でコミットまたは実行してトランザクションを終了しますロールバック操作
Springはデータベースの自動送信をオフにします。メソッドが実行される前に自動送信をオフにし、メソッドの実行後に自動送信をオンにします。
org.springframework.jdbc.datasource.DataSourceTransactionManager.javaソースコードの実装:
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
…………………………
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
…………………………
}
@Override
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
……………………………
// Reset connection.
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
…………………………
}
問題:
自動コミットをオフにした後、トランザクションが完了していない場合、つまり、コミットまたはロールバックが手動で実行されていないときに実行されたSQL操作を処理するにはどうすればよいですか?
C3P0のデフォルトの戦略は、コミットされていないトランザクションをロールバックすることです
。C3P0は、データソースとJNDIバインディングを実装し、JDBC3仕様とJDBC2標準拡張をサポートするオープンソースのJDBC接続プールです。現在使用しているオープンソースプロジェクトには、Hibernate、Springなどがあります
。JNDI(Java Naming and Directory Interface、Java Naming and Directory Interface)は、SUNが提供する標準のJava Naming Systemインターフェイスです。JNDIは、さまざまなアクセスプロバイダーを通じて統合クライアントAPIを提供します。インターフェースJNDIサービス供給インターフェース(SPI)が実装されています。マネージャーはJNDI APIを特定のネーミング・サービスおよびディレクトリー・システムにマップし、Javaアプリケーションがこれらのネーミング・サービスおよびディレクトリー・サービスと対話できるようにします。
4.Springトランザクション機能
すべてのSpringトランザクション管理戦略クラスは、org.springframework.transaction.PlatformTransactionManagerインターフェースから継承します。
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
トランザクション分離レベル:複数の同時トランザクション間の分離度を指します
@Transactional(isolation = Isolation.READ_UNCOMMITTED):コミットされていないデータの読み取り(ダーティ読み取りが発生し、繰り返し不可能な読み取り)は基本的に使用されません
@Transactional(isolation = Isolation.READ_COMMITTED):送信されたデータを読み取ります(繰り返し不可能な読み取りとファントム読み取りが表示されます)
@Transactional(isolation = Isolation.REPEATABLE_READ):繰り返し可能な読み取り(ファントム読み取りが発生します)
@Transactional(isolation = Isolation.SERIALIZABLE):シリアル化
トランザクション伝播動作:現在のトランザクションを開始する前にトランザクションコンテキストがすでに存在する場合、トランザクションメソッドの実行動作を指定するためのいくつかのオプションがあります
TransactionDefinition.PROPAGATION_REQUIRED:
現在トランザクションがある場合は、トランザクションに参加します。現在トランザクションがない場合は、新しいトランザクションを作成します。これがデフォルト値です。TransactionDefinition.PROPAGATION_REQUIRES_NEW:
新しいトランザクションを作成します。現在トランザクションがある場合は、現在のトランザクションを一時停止します。TransactionDefinition.PROPAGATION_SUPPORTS:
現在トランザクションがある場合は、トランザクションに参加します。現在のトランザクションがない場合は、非トランザクション方式で実行を継続します。TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
非トランザクションモードで実行します。現在トランザクションがある場合、現在のトランザクションは一時停止されます。TransactionDefinition.PROPAGATION_NEVER:
非トランザクションモードで実行します。現在トランザクションがある場合、例外がスローされます。TransactionDefinition.PROPAGATION_MANDATORY:
現在トランザクションがある場合は、トランザクションに参加します。現在トランザクションがない場合は、例外がスローされます。TransactionDefinition.PROPAGATION_NESTED:
現在トランザクションがある場合は、現在のトランザクションのネストされたトランザクションとして実行するトランザクションを作成します。現在のトランザクション
がない場合、値はTransactionDefinition.PROPAGATION_REQUIREDと同等です。
上記の表のフィールドの説明:
値:主に異なるトランザクションマネージャーを指定するために使用されます。主に、同じシステム内に異なるトランザクションマネージャーが存在することを確認するために使用されます。たとえば、Springでは、2つのトランザクションマネージャーtxManager1とtxManager2が宣言され、ユーザーはこのパラメーターに基づいて、必要に応じて特定のtxManagerを指定できます。
値適用可能なシナリオ:システムで、複数のデータソースまたは複数のデータベースにアクセスする必要がある場合は、複数のトランザクションマネージャーを構成する必要があります。
REQUIRED_NEW:内部トランザクションは独立して実行され、それぞれのスコープで独立してロールバックまたは送信できます。外部トランザクションは、内部トランザクションのロールバックステータスの影響を受けません。
ESTEDトランザクションは、単一のトランザクションに基づいて管理され、複数のセーブポイントを提供します。複数の保存ポイントのこのメカニズムにより、内部トランザクションの変更によって外部トランザクションのロールバックがトリガーされます。外部トランザクションがロールされた後でも、一部の操作がロールされた場合でも、トランザクション処理を実行し続けることができます。この設定はJDBCセーブポイントに基づいているため、JDBCメカニズムでのみ機能します。
rollbackFor:チェックされた例外をロールバックさせます。つまり、ロールバックされるべきではないロールバック操作を許可します。
noRollbackFor:チェックされていない例外を無視します。つまり、ロールバックする必要のある操作をロールバックしません。
5.その他
トランザクションメソッドのネストされた呼び出しは、トランザクションの伝播を生成します。
Springのトランザクション管理はスレッドセーフです。
親クラスによって宣言された@Transactionalは、サブクラスのすべてのメソッドでトランザクション拡張を実行します。サブクラスオーバーライドは、親クラスをオーバーライドして、@ Transactional宣言構成をオーバーライドします。
@Transactionalはクラス名の上で使用され、クラス内のメソッドは、属性構成を介してクラスの@Transactional構成をオーバーライドできます。たとえば、クラスのグローバル構成は読み取りと書き込みが可能であり、メソッドは次のように変更できます。読み取り専用。
ランタイム例外が処理されない場合、ランタイム例外が発生した後、スレッドが終了するか、メインプログラムが終了します。
終了したくない場合は、すべてのランタイム例外をキャッチし、この処理スレッドを終了させないようにする必要があります。異常なデータがキューに表示されます。通常の処理では、異常なデータを破棄してから、ログを記録する必要があります。以下の正常なデータの処理は、異常なデータの影響を受けないようにする必要があります。
ランタイム以外の例外は、RuntimeException以外の例外であり、すべてExceptionクラスとそのサブクラスに属します。IOException、SQLExceptionなどおよびユーザー定義の例外。この種の例外の場合、JAVAコンパイラはこれらの例外をキャッチして処理するように強制します。そうしないと、プログラムはコンパイルされません。したがって、このような例外が発生した場合、必要かどうかに関係なく、発生する可能性のある例外を処理するために、多くのキャッチブロックしか記述できません。