事前知識
- 詳細な簡単な自己グーグル。
どのようなビジネスですか?
- トランザクションは、アクセスの信頼性、一貫性のある方法で、データベース・プログラム・ユニットを操作します
取引の特徴
- 原子性:行うか行わないのいずれか
- 一貫性:一貫性は、トランザクションが別の一貫した状態にある一貫した状態からデータベースを変換する必要があることを意味し
- 永続性:トランザクションがコミットされると、データベース内のデータを変更することを永続手段は永久的です。
- 単離:互いに異なるトランザクションの動作を妨げることはありません
同時トランザクションの問題
- ダーティ読み取り:ダーティリードトランザクションデータが与えられた上でのデータのために別のにコミットされていないトランザクションを読まれるプロセスを意味します
- 非反復可能読み取り:ポーリング間隔が変更され、別のトランザクションに提出されているため、取引データの範囲内で複数のクエリは、異なる値を返しました。
- マジックリーディング:トランザクションの実行中に複数のクエリのデータ数を意味し、そのような新たなスクリーニングを通して挿入いくつかの他の事項よりも初めて達成第二レベル、データ群の読み出しの魔法のような一貫性のないデータ全体的な
トランザクション分離レベル
- READ_UNCOMMITTED非コミット読み取り:コンテンツ(隔離の最低レベル、高い並列性を)作るためにトランザクションを提出しなかったデータを読み取ることができません
- READ_COMMITTEDを提出読み取り:読み取り専用トランザクションとデータ提出(ロックされた行が読まれて)
- REPEATABLE_READ反復可能読み取り:同じデータが1つのトランザクションで一貫していることを確認するために何回も読みました。他の企業によって影響されることはありません(すべての行をロック読みます)
- SERIALIZABLE連載:順次実行連載取引、
春のサービス
- 春は異なるリソースタイプをサポートするために、統一されたトランザクションAPIを提供します
- 春はまた、宣言的トランザクション管理アプローチ、およびデカップリングビジネスコードを提供しています
- 春は統合のための簡単かつ生態学的なフレームワークであります
- トランザクション管理と同期のサポート、マルチリソース
1.トランザクションの抽象化
1.1 PlatformTransactionManager
- トランザクションマネージャ:コミット操作の主要プロバイダ、ロールバック操作やアクセス統一されたインターフェイスの国政定義トランザクションに従い、
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
1.2 TransactionDefinition
- トランザクション定義:トランザクションに関するいくつかの基本的な情報、などタイムアウト、分離レベル、伝播特性、など
複数のメソッドを渡す方法でトランザクション・コールでは、トランザクション伝播機構、等のような2つのサービスメソッドは、トランザクション操作、トランザクションが処理される方法の別のメソッドを呼び出すための取引方法がありますか?
public interface TransactionDefinition {
// 事务的传播机制
// 如果调用方有事务则使用调用方的事务,如果不存在事务则创建一个事务
int PROPAGATION_REQUIRED = 0;
// 跟随调用方,如果调用方有 那就用,调用方没有那就不用
int PROPAGATION_SUPPORTS = 1;
// 调用方的方法必须运行在一个事务中,不存在事务则抛出异常
int PROPAGATION_MANDATORY = 2;
// 不管调用方是否有事务执行,自己都要起一个新事务。把原先的事务挂起,这个只在jta的事务管理器中起作用(事务是不支持嵌套的)
int PROPAGATION_REQUIRES_NEW = 3;
// 即使调用方有事务,我也要在非事务中执行,把原先的事务挂起
int PROPAGATION_NOT_SUPPORTED = 4;
// 绝不在事务里面执行
int PROPAGATION_NEVER = 5;
// 嵌套事务(实际是不支持的,是利用存盘点来实现,把调用方之前执行的存盘点,然后类似于再开启一个事务执行,执行完毕后恢复存盘点,继续执行。JDBC3.0以上支持)
int PROPAGATION_NESTED = 6;
// 以下定义的是事务的隔离机制
// 根据数据库的默认的隔离机制而定
int ISOLATION_DEFAULT = -1;
// 读未提交
int ISOLATION_READ_UNCOMMITTED = 1; // same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
// 读已提交
int ISOLATION_READ_COMMITTED = 2; // same as java.sql.Connection.TRANSACTION_READ_COMMITTED;
// 可重复读
int ISOLATION_REPEATABLE_READ = 4; // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;
// 串行化
int ISOLATION_SERIALIZABLE = 8; // same as java.sql.Connection.TRANSACTION_SERIALIZABLE;
// 默认超时时间,以数据库设定为准
int TIMEOUT_DEFAULT = -1;
// 获取事务的传播属性
default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}
// 获取事务的隔离级别
default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}
// 获取事务超时时间
default int getTimeout() {
return TIMEOUT_DEFAULT;
}
// 事务是否只读。
default boolean isReadOnly() {
return false;
}
// 获取事务的名称
@Nullable
default String getName() {
return null;
}
// 返回一个默认事务定义
static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}
}
1.3 TransactionStatus
- 事務の状態:それはロールバック用にマークされているかどうか、このような新しいトランザクションかどうかなど、いくつかの状態情報の問題、
// Savepoiont就是在Nested这种传播机制中提供保存点机制来实现嵌套事务,出错的时候可以选择恢复到保存点
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
// 是否有保存点
boolean hasSavepoint();
@Override
void flush();
}
public interface TransactionExecution {
// 是否是一个新事务
boolean isNewTransaction();
// 设置事务回滚
void setRollbackOnly();
// 事务是否回滚
boolean isRollbackOnly();
// 获取事务是否完成
boolean isCompleted();
}
2. PlatformTransactionManager一般的な実装
- DataSourceTransactionManager(JDBCテンプレートの、MyBatisのその他)
- (データJPA、休止、等)JpaTransactionManager
- JmsTransactionManagerその(メッセージミドルウェア用)
- (主に分散トランザクション用)JtaTransactionManager
3.スプリング単一のデータソーストランザクション実用例
3.1環境説明:
- データソース:アリババドルイド
- データベース:MySQLの5.7
- SpringBoot:2.2.2.RELEASE
- ORM:MyBatisの
- GitHubの:https://github.com/ilssio/transaction-example/tree/master/one-data-source
コード例3.2
- PlatformTransactionManagerを達成するために、転送業務の量に基づいて、この場合はDataSourceTransactionManagerです
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDAO accountDAO;
@Autowired
PlatformTransactionManager transactionManager;
/**
* 声明式事务
* propagation = Propagation.REQUIRED (默认值就是REQUIRED) 如果调用方有事务就直接使用调用方的事务,如果没有就新建一个事务
* transactionManager = "transactionManager" 也是默认值
* isolation= Isolation.DEFAULT 隔离级别
* 还有timeout等参数 可自行查看Transactional的源码 里面都有说明
* @param sourceAccountId 源账户
* @param targetAccountId 目标账户
* @param amount 金额
* @return 操作结果信息
*/
@Override
@Transactional(transactionManager = "transactionManager",propagation = Propagation.REQUIRED, rollbackFor = Exception.class, isolation= Isolation.DEFAULT)
public String transferAnnotation(Long sourceAccountId, Long targetAccountId, BigDecimal amount) {
AccountDO sourceAccountDO = accountDAO.selectByPrimaryKey(sourceAccountId);
AccountDO targetAccountDO = accountDAO.selectByPrimaryKey(targetAccountId);
if (null == sourceAccountDO || null == targetAccountDO) {
return "转入或者转出账户不存在";
}
if (sourceAccountDO.getBalance().compareTo(amount) < 0) {
return "转出账户余额不足";
}
sourceAccountDO.setBalance(sourceAccountDO.getBalance().subtract(amount));
accountDAO.updateByPrimaryKeySelective(sourceAccountDO);
// error("annotation error!");
targetAccountDO.setBalance(targetAccountDO.getBalance().add(amount));
accountDAO.updateByPrimaryKeySelective(targetAccountDO);
return "转账成功!";
}
@Override
public String transferCode(Long sourceAccountId, Long targetAccountId, BigDecimal amount) {
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
// 获取事务 开始业务执行
TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
try {
AccountDO targetAccountDO = accountDAO.selectByPrimaryKey(targetAccountId);
AccountDO sourceAccountDO = accountDAO.selectByPrimaryKey(sourceAccountId);
if (null == sourceAccountDO || null == targetAccountDO) {
return "转入或者转出账户不存在";
}
error("code error");
if (sourceAccountDO.getBalance().compareTo(amount) < 0) {
return "转出账户余额不足";
}
sourceAccountDO.setBalance(sourceAccountDO.getBalance().subtract(amount));
targetAccountDO.setBalance(targetAccountDO.getBalance().add(amount));
accountDAO.updateByPrimaryKeySelective(sourceAccountDO);
accountDAO.updateByPrimaryKeySelective(sourceAccountDO);
// 提交事务
transactionManager.commit(transaction);
return "转账成功!";
} catch (Exception e) {
log.error("转账发生错误,开始回滚,source: {}, target: {}, amount: {}, errMsg: {}",
sourceAccountId, targetAccountId, amount, e.getMessage());
// 报错回滚
transactionManager.rollback(transaction);
}
return "转账失败";
}
@Override
public List<AccountDO> listAll() {
return accountDAO.selectAll();
}
private static void error(String msg) {
throw new RuntimeException(msg);
}
}
3.3トランザクション注釈トランザクションを実装
-
注釈トランザクションを使用し、春にはプロキシエージェントの実装クラスを使用します、
-
トランザクションマネージャは、コンテキストから得られた、ビジネストランザクション開口コードの実装、
-
あなたはロールバック例外条件を設定した場合、ロールバックの実装に、満足しています
-
我々は呼んサービスメソッドは直接呼び出しです
-
しかし、春の実現に、実際の取引顧問は(トランザクション管理を行う)が呼び出されると、ハンドルサービスにAOPプロキシ(AOPプロキシサービス)による方法注釈付きのトランザクションを呼び出します
- トリガーに転送インタフェース/ API /アカウント/転送/注釈を介して?ソース= 1&ターゲット= 2&量= 123個のアカウントは1 2 123ドル、転送動作を占め
- ログ情報(開かれたDEBUGログ):
o.s.web.servlet.DispatcherServlet : GET "/api/account/transfer/annotation?source=1&target=2&amount=123", parameters={masked}
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to io.ilss.transaction.onedatasource.web.AccountController#transferAnnotation(Long, Long, String)
o.s.j.d.DataSourceTransactionManager : Creating new transaction with name [io.ilss.transaction.onedatasource.service.impl.AccountServiceImpl.transferAnnotation]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; 'transactionManager',-java.lang.Exception
o.s.j.d.DataSourceTransactionManager : Acquired Connection [com.mysql.jdbc.JDBC4Connection@185f0a96] for JDBC transaction
o.s.j.d.DataSourceTransactionManager : Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@185f0a96] to manual commit
org.mybatis.spring.SqlSessionUtils : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42]
o.m.s.t.SpringManagedTransaction : JDBC Connection [com.mysql.jdbc.JDBC4Connection@185f0a96] will be managed by Spring
i.i.t.o.d.AccountDAO.selectByPrimaryKey : ==> Preparing: select id, nickname, username, `password`, balance, create_time, update_time from account where id = ?
i.i.t.o.d.AccountDAO.selectByPrimaryKey : ==> Parameters: 1(Long)
i.i.t.o.d.AccountDAO.selectByPrimaryKey : <== Total: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42]
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42] from current transaction
i.i.t.o.d.AccountDAO.selectByPrimaryKey : ==> Preparing: select id, nickname, username, `password`, balance, create_time, update_time from account where id = ?
i.i.t.o.d.AccountDAO.selectByPrimaryKey : ==> Parameters: 2(Long)
i.i.t.o.d.AccountDAO.selectByPrimaryKey : <== Total: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42]
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42] from current transaction
i.i.t.o.d.A.updateByPrimaryKeySelective : ==> Preparing: update account SET nickname = ?, username = ?, `password` = ?, balance = ?, create_time = ?, update_time = ? where id = ?
i.i.t.o.d.A.updateByPrimaryKeySelective : ==> Parameters: 小一(String), xiaoyi(String), 123456(String), 877.00(BigDecimal), 2020-01-09T17:04:28(LocalDateTime), 2020-01-09T17:44:33(LocalDateTime), 1(Long)
i.i.t.o.d.A.updateByPrimaryKeySelective : <== Updates: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42]
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42] from current transaction
i.i.t.o.d.A.updateByPrimaryKeySelective : ==> Preparing: update account SET nickname = ?, username = ?, `password` = ?, balance = ?, create_time = ?, update_time = ? where id = ?
i.i.t.o.d.A.updateByPrimaryKeySelective : ==> Parameters: 小二(String), xiaoer(String), 123456(String), 223.00(BigDecimal), 2020-01-09T17:04:40(LocalDateTime), 2020-01-09T17:04:40(LocalDateTime), 2(Long)
i.i.t.o.d.A.updateByPrimaryKeySelective : <== Updates: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@39615e42]
o.s.j.d.DataSourceTransactionManager : Initiating transaction commit
o.s.j.d.DataSourceTransactionManager : Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@185f0a96]
o.s.j.d.DataSourceTransactionManager : Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@185f0a96] after transaction
m.m.a.RequestResponseBodyMethodProcessor : Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
m.m.a.RequestResponseBodyMethodProcessor : Writing ["转账成功!"]
o.s.web.servlet.DispatcherServlet : Completed 200 OK
- 正常に実行されます
- / API /アカウント/転送/注釈サービスにGETリクエストは、インターフェイスメソッドを呼び出します
- 書かれたトランザクションのアノテーションの設定に従って新しいトランザクションを作成します。
- その後、選択JDBC接続とSQLSESSIONを作成
- 同期SQLSESSIONの登録
- SQL操作
- そして、トランザクションのコミット
- リソースの最後のリリース。完了メソッドの呼び出し
- インターフェイスは200を返さ
-
ログには、私たちが実際に使用MyBatisの+ドルイドコンフィギュレーション・データ・ソースのトランザクション管理DataSourceTransactionManagerを使用していることがわかります。
-
コードはトランザクションマネージャーが指定されているが、実際には、他の関連するトランザクション管理コードを増やすか、ビーンのデータソースが登録されていませんでした。春、私はトランザクションマネージャーのTransactionManager自動アセンブリ名がこれです設定しているため、この場所は、あなたがそれを行う手助けをすることですので。名前を設定するには、この構成では、構成マネージャを持って書かれたリマインダーは、この取引の実現であることを示唆しています。一言で言えば、指定PlatformTransactionManagerです。複数のデータソースの後ろに私たちは、トランザクションマネージャーは、指定された別のトランザクション管理によって達成されるとき。
-
今のようなエラーがあれば?
-
ログ(オープンDEBUGログ)を見てください:
o.s.web.servlet.DispatcherServlet : GET "/api/account/transfer/annotation?source=1&target=2&amount=123", parameters={masked}
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to io.ilss.transaction.onedatasource.web.AccountController#transferAnnotation(Long, Long, String)
o.s.j.d.DataSourceTransactionManager : Creating new transaction with name [io.ilss.transaction.onedatasource.service.impl.AccountServiceImpl.transferAnnotation]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; 'transactionManager',-java.lang.Exception
o.s.j.d.DataSourceTransactionManager : Acquired Connection [com.mysql.jdbc.JDBC4Connection@1b6963ec] for JDBC transaction
o.s.j.d.DataSourceTransactionManager : Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@1b6963ec] to manual commit
org.mybatis.spring.SqlSessionUtils : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@66240e58]
o.m.s.t.SpringManagedTransaction : JDBC Connection [com.mysql.jdbc.JDBC4Connection@1b6963ec] will be managed by Spring
i.i.t.o.d.AccountDAO.selectByPrimaryKey : ==> Preparing: select id, nickname, username, `password`, balance, create_time, update_time from account where id = ?
i.i.t.o.d.AccountDAO.selectByPrimaryKey : ==> Parameters: 1(Long)
i.i.t.o.d.AccountDAO.selectByPrimaryKey : <== Total: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@66240e58]
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@66240e58] from current transaction
i.i.t.o.d.AccountDAO.selectByPrimaryKey : ==> Preparing: select id, nickname, username, `password`, balance, create_time, update_time from account where id = ?
i.i.t.o.d.AccountDAO.selectByPrimaryKey : ==> Parameters: 2(Long)
i.i.t.o.d.AccountDAO.selectByPrimaryKey : <== Total: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@66240e58]
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@66240e58] from current transaction
i.i.t.o.d.A.updateByPrimaryKeySelective : ==> Preparing: update account SET nickname = ?, username = ?, `password` = ?, balance = ?, create_time = ?, update_time = ? where id = ?
i.i.t.o.d.A.updateByPrimaryKeySelective : ==> Parameters: 小一(String), xiaoyi(String), 123456(String), 754.00(BigDecimal), 2020-01-09T17:04:28(LocalDateTime), 2020-01-09T17:44:33(LocalDateTime), 1(Long)
i.i.t.o.d.A.updateByPrimaryKeySelective : <== Updates: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@66240e58]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@66240e58]
org.mybatis.spring.SqlSessionUtils : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@66240e58]
o.s.j.d.DataSourceTransactionManager : Initiating transaction rollback
o.s.j.d.DataSourceTransactionManager : Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@1b6963ec]
o.s.j.d.DataSourceTransactionManager : Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1b6963ec] after transaction
o.s.web.servlet.DispatcherServlet : Failed to complete request: java.lang.RuntimeException: annotation error!
o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is
java.lang.RuntimeException: annotation error!
.......
o.a.c.c.C.[Tomcat].[localhost] : Processing ErrorPage[errorCode=0, location=/error]
o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/error?source=1&target=2&amount=123", parameters={masked}
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)
o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, text/html;q=0.8]
o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 500
- 失敗した実行
- / API /アカウント/転送/注釈サービスにGETリクエストは、インターフェイスメソッドを呼び出します
- 書かれたトランザクションのアノテーションの設定に従って新しいトランザクションを作成します。
- その後、選択JDBC接続とSQLSESSIONを作成
- 同期SQLSESSIONの登録
- SQL:あなたが最初のエラーは、新しいオペレーティングで発生した実行、ログから見ることができます
- 接続の直接のロールバックロールバックJDBCトランザクションは、エラーログを印刷します。第2の更新操作は行ってしまったの後ろ
- メソッド異常終了し、インターフェース500
3.4プログラミングトランザクション管理
- 主な手順プログラムによるトランザクション
- トランザクション定義TransactionDefinition transactionDefinition =新しいDefaultTransactionDefinitionを作成します();
- トランザクションのプロパティの設定:そのような伝播、隔離およびその他のプロパティなどのプロパティ
- オープントランザクションのTransactionManager
- ビジネスコード実行
- トランザクション/ロールバック処理をコミット
- 同じ基本的かつ宣言的トランザクションのこのインスタンスのプログラムによるトランザクションの実装は、自分自身の自己実現のためのコードをダウンロードすることができます興味を持っています。
// TODO:複数のデータソースのトランザクション管理の下で
- https://blog.csdn.net/ilo114/article/details/103937126
私について
- 杭州、通常の学部読み取り、コンピュータ科学と技術的専門知識、仕事を探してクレイジー6月20日卒業し、座標。。。。
- あなた首長光スプレーは狂った兄が学習され、新人段階に現在あります。
- 私はあなたと交換アヒルを歓迎します!!!