背景
インターネットバンキングと電力供給産業は、ほとんどのデータベーストランザクションを懸念しています。
コアビジネス | 説明 |
---|---|
金融業界 - 金融商品の量 | これは、エラーを許可していません。 |
電気サプライヤー業界 - 商品取引の金額、商品の在庫 | これは、エラーを許可していません。 |
困難に直面して:
高同時保証:データの一貫性、高性能、
物事の春の治療:
AOPは、繰り返しのtry-catch-最後に加えてコードにトランザクションのサポート、宣言型トランザクションコードを提供する技術を使用しています。
シーン2つのソリューション:
シーン | ソリューション |
---|---|
在庫控除、取引記録は、データの一貫性の量を占めます | 一貫性を確保するためのデータベースのトランザクション |
タスクの失敗のバッチ処理部は、ロールバックバッチジョブには影響を与えません。 | データベーストランザクション伝播の振る舞い |
JDBC加工サービス
コード
package com.springbootpractice.demo.demo_jdbc_tx.biz;
import lombok.SneakyThrows;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Objects;
import java.util.Optional;
/**
* 说明:代码方式事务编程 VS 申明式事物编程
* @author carter
* 创建时间: 2020年01月08日 11:02 上午
**/
@Service
public class TxJdbcBiz {
private final JdbcTemplate jdbcTemplate;
public TxJdbcBiz(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@SneakyThrows
public int insertUserLogin(String username, String note) {
Connection connection = null;
int result = 0;
try {
connection = Objects.requireNonNull(jdbcTemplate.getDataSource()).getConnection();
connection.setAutoCommit(false);
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
final PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO user_login(user_name,password,sex,note) VALUES(?,?,?,?)");
preparedStatement.setString(1, username);
preparedStatement.setString(2, "abc123");
preparedStatement.setInt(3, 1);
preparedStatement.setString(4, note);
result = preparedStatement.executeUpdate();
connection.commit();
} catch (Exception e) {
Optional.ofNullable(connection)
.ifPresent(item -> {
try {
item.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
});
e.printStackTrace();
} finally {
Optional.ofNullable(connection)
.filter(this::closeConnection)
.ifPresent(item -> {
try {
item.close();
} catch (SQLException e) {
e.printStackTrace();
}
});
}
return result;
}
private boolean closeConnection(Connection item) {
try {
return !item.isClosed();
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
@Transactional
public int insertUserLoginTransaction(String username, String note) {
String sql = "INSERT INTO user_login(user_name,password,sex,note) VALUES(?,?,?,?)";
Object[] params = {username, "abc123", 1, note};
return jdbcTemplate.update(sql, params);
}
}
テストコード
package com.springbootpractice.demo.demo_jdbc_tx;
import com.springbootpractice.demo.demo_jdbc_tx.biz.TxJdbcBiz;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.TransactionManager;
import org.springframework.util.Assert;
@SpringBootTest
class DemoJdbcTxApplicationTests {
@Autowired
private TxJdbcBiz txJdbcBiz;
@Autowired
private TransactionManager transactionManager;
@Test
void testInsertUserTest() {
final int result = txJdbcBiz.insertUserLogin("monika.smith", "xxxx");
Assert.isTrue(result > 0, "插入失败");
}
@Test
void insertUserLoginTransactionTest() {
final int result = txJdbcBiz.insertUserLoginTransaction("stefan.li", "hello transaction");
Assert.isTrue(result > 0, "插入失败");
}
@Test
void transactionManagerTest() {
System.out.println(transactionManager.getClass().getName());
}
}
コードは、非常に厄介な場所を持っているのtry-catch-ようやくです。
フローチャート
全体的なプロセスとAOPプロセスはAOPの使用に非常に似ている、あなたは、SQLの手順を実行することができ、通知を行うための場所で一人で、他の固定のプロセスを達成抽出しました。
肯定トランザクション
トランザクション宣言型注釈@Transactionをマークするには、またはクラスの標準的な方法ことができます。
@Tranaction使用位置 | 説明 |
---|---|
クラスまたはインタフェース | トランザクションを可能にするすべての公共非静的メソッドのクラスは、エージェントインタフェースの発効に基づくとき、実装クラス上の春の勧告は、AOPは、そうでない効果を取る必要があります |
方法論 | この方法 |
@Transactionソースコードおよび設定項目
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.transaction.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default -1;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
説明:
プロパティ | 説明 |
---|---|
隔離 | トランザクション分離レベル |
伝搬 | 伝播挙動 |
rollbackFor、rollbakcForClassName | 異常なトランザクションのロールバックをトリガします |
値 | トランザクションマネージャ |
タイムアウト | トランザクションのタイムアウト |
読み取り専用 | それは、読取り専用トランザクションであるかどうか |
noRollbackFor、noRollbackForClassName | どのような例外は、トランザクションのロールバックをトリガしません。 |
インストールプロセスの総務:
springIOCコンテナ起動し、意志@Transactional注釈解析された構成情報、およびトランザクションの定義(TransactionDefinition)保存して、トランザクションを実行するために採用するかを戦略、トランザクションを開始する必要がどのような種類の記録。
私たちがしなければならないことは@Transactionalと構成プロパティをすることができマークです。
プロセス図:
大幅に簡略化方法を使用して、
コード
@Transactional
public int insertUserLoginTransaction(String username, String note) {
String sql = "INSERT INTO user_login(user_name,password,sex,note) VALUES(?,?,?,?)";
Object[] params = {username, "abc123", 1, note};
return jdbcTemplate.update(sql, params);
}
トランザクションマネージャ
オープントランザクション、コミットは、ロールバックは、トランザクションマネージャ上に置かれています。トランザクションマネージャー;
トランザクションマネージャーコード
package org.springframework.transaction;
public interface TransactionManager {
}
これは空のインターフェース、実際の作業がPlatfromTransactionManagerあります。
PlatfromTransactionManagerコード:
package org.springframework.transaction;
import org.springframework.lang.Nullable;
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
void commit(TransactionStatus var1) throws TransactionException;
void rollback(TransactionStatus var1) throws TransactionException;
}
3つの棚トランザクションマネージャの比較:
棚 | トランザクション管理 | 説明 |
---|---|---|
春-JDBC | DatasourceTransactionManager | |
JPA | JpaTransactionManager | |
MyBatisの | DatasourceTransactionManager |
コード例のポイントは私MyBatisの!
トランザクション分離レベル
シーン:電気サプライヤー業界の在庫控除、時間がマルチスレッド環境控除の株式で、データベースのために、同じレコードへ複数のトランザクションの同僚へのアクセスがあるでしょう、この場合による一貫性のないデータは、損失がデータベースが更新されます。
データベースのトランザクション4つのプロパティ
そのACID
取引の特徴 | 英語名 | 説明 |
---|---|---|
不可分性 | アトミック | ステップA、B、C、複数を含むトランザクションは、これらのアトミック・オペレーションの識別が成功するすべての問題であるか、またはすべてが失敗し、第三のケースではないであろう |
一貫性 | 一貫性 | トランザクションは、一貫性のある状態では、すべてのデータを完了した後、 |
隔離 | 隔離 | バリア特性を提供することにより、分離レベルが与えられると、失われた更新の抑制生成するために、異なるトランザクションにおいて、各スレッドが同じデータにアクセスする複数のスレッドを処理する、の存在は、選択された失われた更新の発生を抑制することができます |
持久性 | 耐久性 | 停電でもプログラムを継続して使用するために提供することができた後のトランザクションの後、データは、再起動を持続します |
分離レベル:
分離レベル | 説明 | 問題 | 同時パフォーマンス |
---|---|---|---|
非コミット読み取り[コミットされていない読みます] | さらに、データはトランザクションが適用できない要件である、トランザクションがコミットされていないトランザクションを読み取ることができますが比較的高く、あまり厳しいシナリオに適したトランザクション | ダーティ読み取り(シングル) | 同時最高のパフォーマンス |
コミット読む[コミットを読みます] | トランザクションは提出されたデータを持っている別のトランザクションを読み取ることができます | 非反復可能読み取り(シングル) | 一般的には同時パフォーマンス |
反復可能読み取り]を[繰り返し読み取り | トランザクションは、最新の値が変化するかどうかを決定するために提出された場合 | (複数のデータの点で)ファンタジー読み出し | 同時パフォーマンスの低下 |
シリアライズ[直列化可能] | すべてのSQLは、順番に実行されています | まったく同じデータ | 同時最悪のパフォーマンス |
基づいて選択
分離レベル | ダーティー読み取り | 非反復可能読み取り | ファントム読み取り |
---|---|---|---|
非コミット読み取り | それはあります | それはあります | それはあります |
コミット読取り | ノー | それはあります | それはあります |
反復可能読み取り | ノー | ノー | それはあります |
連載 | ノー | ノー | ノー |
実際のシーンに応じてトランザクション分離レベルを設定することを可能にします。
分離レベルは、ロックのコストをもたらすでしょう。最適化:
- オプティミスティック・ロック、
- 分散ロックはRedisの、
- 分散ロックをZK。
データベース | トランザクション分離レベル | デフォルトのトランザクション分離レベル |
---|---|---|
mysqlの | 四種類 | 反復可能読み取り |
オラクル | コミット、連載を読みます | コミット読取り |
springboot構成アプリケーションのデフォルトのトランザクション分離レベル:spring.datasource.xxx.default-トランザクション分離= 2
デジタル | 対応する分離レベル |
---|---|
-1 | ノー |
1 | 非コミット読み取り |
2 | コミット読取り |
4 | 反復可能読み取り |
8 | 連載 |
トランザクション伝播の挙動
コミュニケーション行動は取られたアプローチ間のトランザクションを呼び出すための政策課題です。シーン:;サブタスクのロールバックロールバックは、トランザクションがAさんには影響しませんトランザクションAのタスクでバッチは、各個人が取引Bnとされる別のトランザクションを持っています。
ソース・伝播挙動
package org.springframework.transaction.annotation;
import org.springframework.transaction.TransactionDefinition;
public enum Propagation {
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
NEVER(TransactionDefinition.PROPAGATION_NEVER),
NESTED(TransactionDefinition.PROPAGATION_NESTED);
private final int value;
Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
7スプレッドの構成プロパティが含まれ、以下で説明されています。
伝播挙動 | 親メソッド事務の存在下での子供の行動方法 | サブメソッドのトランザクションの振る舞いは、親メソッドに存在しません |
---|---|---|
必須 | デフォルトの伝播挙動は、続きます | 新しいトランザクションを作成します。 |
SUPPORTS | フォロー。 | いいえ取引なし、トランザクションサブ方法 |
MANDATORY | フォロー | 例外を投げます |
REQUIRES_NEW | 新しいトランザクションを作成します。 | 新しいトランザクションを作成します。 |
サポートされていません | 保留中のトランザクション、実行サブ方法 | いいえ取引、実行サブ方法ありません |
決して | スロー | いいえ、実行方法情勢サブません |
NESTED | サブメソッドの例外は、バック親ロールなしのロールバックSQL子の唯一の方法でトランザクションメソッドを発生します | 例外は、SQLのロールバックのみサブ方法、親メソッドとは何の関係も発生しません |
3つの一般的な通信の挙動:
- 必須
- REQUIRES_NEW
- NESTED
これらの3つのテストのコード伝播の挙動:
spring使用了save point的技术来让子事务回滚,而父事务不会滚;如果不支持save point,则新建一个事务来运行子事务;
区别点 | RequestNew | Nested |
---|---|---|
传递 | 拥有自己的锁和隔离级别 | 沿用父事务的隔离级别和锁 |
@Transaction自调用失效问题
事务的实现原理是基于AOP,同一个类中方法的互相调用,是自己调用自己,而没有代理对象的产生,就不会用到aop,所以,事务会失效; **解决办法:**通过spring的ioc容器得到当前类的代理对象,调用本类的方法解决; 原创不易,转载请注明出处。