001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.db; 017 018import java.sql.Connection; 019import java.util.Locale; 020import java.util.concurrent.ConcurrentMap; // 6.4.3.4 (2016/03/11) 021import java.util.concurrent.ConcurrentHashMap; // 6.4.3.4 (2016/03/11) 022 023import org.opengion.fukurou.system.Closer; 024import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 025import static org.opengion.fukurou.system.HybsConst.CR ; // 6.3.6.1 (2015/08/28) 026 027/** 028 * コネクションを共有して、トランザクションを実現します。 029 * 030 * 基本的には、TransactionTag で利用されますが、一部、このオブジェクトを 031 * 渡して、直接、利用するケースもあります。 032 * 033 * トランザクションがすべて完了した後で、realClose() メソッドを呼び出します。 034 * 一度でも、rollback が指定されていれば、ロールバックを行い、コネクションを 035 * 破棄します。それ以外で、commit が指定されていれば、コミットを行い、 036 * コネクションを、プールに戻します。どちらも指定されていなければ、 037 * コネクションプールに戻すだけになります。 038 * 039 * 6.3.6.1 (2015/08/28) 040 * selectを実行した後で明示的にcommit,rollbackを行わないのはOracle位 041 * らしいので、検索終了時でも、commit か、rollback を行うようにします。 042 * つまり、commit されない(=途中で処理が打ち切られた)場合は、 043 * rollback するように仕様変更しますので、Transactionオブジェクトを 044 * 呼び出した処理の最後には、検索であろうとなかろうと、commit()を入れてください。 045 * ただし、Transaction オブジェクトは、DBアクセス以外にも適用可能に 046 * 作成しているため、Connection がある場合のみ、実際の commit/rollback が 047 * 実行されます。 048 * 049 * 6.3.6.1 (2015/08/28) 050 * 一度、finish() を実行すると、次回実行時にエラーにします。 051 * 6.3.9.0 (2015/11/06) 052 * synchronized メソッドをsynchronizedブロックに変更。 053 * 054 * 考え方として、下記のような流れになります。 055 * <pre> 056 * Transaction tran = new TransactionImpl/Real( appInfo ) ; 057 * try { 058 * ・・・・・ 059 * tran.commit(); 060 * } 061 * catch( final Exception ex ) { 062 * tran.rollback(); 063 * } 064 * finally { 065 * tran.close(); // TransactionReal の場合 066 * tran.finish(); // TransactionImpl の場合 067 * } 068 * </pre> 069 * 070 * 6.3.6.1 (2015/08/28) 071 * AutoCloseableを使用したtry-with-resources 構文を使用した場合。close/finish 不要。 072 * <pre> 073 * try( Transaction tran = new TransactionImpl/Real( appInfo ) ) { 074 * ・・・・・ 075 * tran.commit(); 076 * } 077 * ただし、処理自体がアベンドしないケースでは、rollback() を、自分で呼ぶか、commit() しない(=rollback()される)ようにします。 078 * </pre> 079 * 080 * @og.rev 5.1.9.0 (2010/08/01) 新規作成 081 * 082 * @version 5.0 083 * @author Kazuhiko Hasegawa 084 * @since JDK6.0, 085 */ 086public class TransactionImpl implements Transaction { 087 /** このプログラムのVERSION文字列を設定します。 {@value} */ 088 private static final String VERSION = "6.4.3.4 (2016/03/11)" ; 089 private static final long serialVersionUID = 643420160311L ; 090 091 private static final String DBID = "DEFAULT"; 092 private final ApplicationInfo appInfo ; 093 094 private Connection defconn ; // 最も利用率の高いDEFAULTだけ、別に変数を用意。 095 096 /** 6.4.3.4 (2016/03/11) ConcurrentHashMap で同期処理を行います。 */ 097 private final ConcurrentMap<String,Connection> dbidMap = new ConcurrentHashMap<>(); // 6.4.3.4 (2016/03/11) 098 private boolean isCommit ; // commit() されたかどうか。 099 private boolean isEndCommit ; // 6.4.3.3 (2016/03/04) doEndTag() が実行されたかどうか。 100 private boolean isRollback ; // rollback() されたかどうか。 101 private boolean isFinish ; // 処理の最後にセットします。 102 103 /** 104 * ApplicationInfo を指定して作成する、コンストラクター 105 * 106 * このクラスは、基本的には、TransactionTag クラスから作成されます。 107 * 108 * @param appInfo 内部統制用のアクセス情報 109 */ 110 public TransactionImpl( final ApplicationInfo appInfo ) { 111 this.appInfo = appInfo ; 112 } 113 114 /** 115 * 指定のDBID に対応した、Connection オブジェクトを返します。 116 * 内部Mapに存在していれば、そのコネクションを、存在しなければ、 117 * 新しく作成します。 118 * 119 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 120 * @og.rev 6.4.3.4 (2016/03/11) Map#computeIfAbsent で対応する。 121 * 122 * @param dbid 接続先ID 123 * 124 * @return 指定のDBID に対応した、Connectionオブジェクト 125 */ 126 @Override // Transaction 127 public Connection getConnection( final String dbid ) { 128 // 8.5.5.1 (2024/02/29) PMD 7.0.0 OnlyOneReturn メソッドには終了ポイントが 1 つだけ必要 129 final Connection conn ; 130 131 if( dbid == null || dbid.isEmpty() || DBID.equalsIgnoreCase( dbid ) ) { 132 if( defconn == null ) { 133 defconn = ConnectionFactory.connection( DBID,appInfo ); 134 } 135// return defconn; 136 conn = defconn; 137 } 138 else { 139 final String udbid = dbid.toUpperCase( Locale.JAPAN ); // 大文字化 140 // Map#computeIfAbsent : 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし 141// return dbidMap.computeIfAbsent( udbid , k -> ConnectionFactory.connection( udbid,appInfo ) ); 142 conn = dbidMap.computeIfAbsent( udbid , k -> ConnectionFactory.connection( udbid,appInfo ) ); 143 } 144 return conn; 145 } 146 147 /** 148 * コミット処理が行われた場合に、内部フラグ(isCommit)を true にセットします。 149 * 1回でもコミットが行われており、ロールバックが行われていなければ、 150 * コミットされます。 151 * 152 * 検索処理時でも、最後に commit() を実行してください。実行されていない場合は、 153 * 自動的に、rollback() が、実行されます。 154 * 155 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。return 不要。 156 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 157 * 158 */ 159 @Override // Transaction 160 public void commit() { 161 isCommit = true; 162 } 163 164 /** 165 * ロールバック処理が行われた場合に、内部フラグ(isRollback)を true にセットします。 166 * 1回でもロールバックが行われていれば、最終的にはロールバックされます。 167 * 168 * 一度も、ロールバックが行われていない場合でも、コミットが行われていない場合は、 169 * rollback()を実行します。 170 * 171 * 172 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。return 不要。 173 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 174 * 175 */ 176 @Override // Transaction 177 public void rollback() { 178 isRollback = true; 179 } 180 181 /** 182 * トランザクションの、終了時処理を行います。 183 * 184 * それまでの処理は、すべて正常に処理できた場合に、使用します。 185 * 186 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。return 不要。 187 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 188 * 189 * @see AutoCloseable#close() 190 */ 191 @Override // AutoCloseable 192 public void close() { 193 // Impl は、finish() で、実質的な完了処理を行う。 194 } 195 196 /** 197 * 最終的なコミットが行われた場合に、内部フラグ(isEndCommit)を true にセットします。 198 * 通常は、この処理は、1値度だけ実行されます。 199 * 初期値が、false なので、途中で一度でも実行されると、true にセットされ、最後まで 200 * 処理されたとみなされてしまうので、注意してください。 201 * 202 * 通常は、タグリブの、doEndTag() が実行された場合に、呼びます。 203 * このフラグが、true でないと(つまり、一度でも呼ばれないと)最終的に、commit されません。 204 * 205 * なお、endCommit() が呼ばれると、自動的に、commit() も呼んでおきます。 206 * 207 * @og.rev 6.4.3.3 (2016/03/04) 一般的なタグで、SKIP_PAGE された場合、rollback するようにします。 208 */ 209 @Override // Transaction 210 public void endCommit() { 211 isEndCommit = true ; 212 isCommit = true ; 213 } 214 215 /** 216 * トランザクションとして、終了時処理を行います。 217 * 218 * トランザクションがすべて完了した後で、呼び出します。 219 * 一度でも、Rollback が指定されていれば、ロールバックを行い、コネクションを 220 * 破棄します。それ以外で、Commit が指定されていれば、コミットを行い、 221 * コネクションを、プールに戻します。どちらも指定されていなければ、 222 * コネクションプールに戻すだけになります。 223 * 224 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。メソッド名変更。 225 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 226 * @og.rev 6.4.3.4 (2016/03/11) Map#computeIfAbsent で対応する。 227 */ 228 public void finish() { 229 if( isFinish ) { 230 final String errMsg = "すでに、finish() 実行済みです。" + CR 231 + " 新規に Transaction オブジェクトの作成から行ってください。" + CR ; 232 throw new OgRuntimeException( errMsg ); 233 } 234 235 if( defconn != null ) { 236 connClose( defconn,DBID ); 237 defconn = null; // 内部変数を初期化します。 238 } 239 240 // Map#computeIfAbsent : 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし 241 dbidMap.forEach( (dbid,conn) -> connClose( conn,dbid ) ); 242 dbidMap.clear(); 243 244 isFinish = true; // 次回実行時にエラーにします。 245 } 246 247 /** 248 * Connection オブジェクトをクローズします。 249 * 250 * 251 * commit されており、rollback されていない場合のみ、commit します。 252 * それ以外は、rollback します。 253 * rollback された場合は、コネクションプールに戻さずに、破棄します。 254 * 本来は、その、Connection に問題はないかもしれませんが、あえて、 255 * キャッシュを継続させる必要もないと考えます。 256 * 257 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。内部処理見直し。 258 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 259 * @og.rev 6.4.3.3 (2016/03/04) 一般的なタグで、SKIP_PAGE された場合、rollback するようにします。 260 * 261 * @param conn クローズ処理を行う、Connectionオブジェクト 262 * @param dbid 接続先ID 263 */ 264 private void connClose( final Connection conn, final String dbid ) { 265 // commit されており、rollback されていない場合 266 final boolean isOK ; 267 if( isCommit && isEndCommit && !isRollback ) { // 6.4.3.3 (2016/03/04) 268 isOK = Closer.commit( conn ); 269 } 270 else { 271 isOK = Closer.rollback( conn ); 272 } 273 274 // rollback されているか、commit/rollback でエラーが発生した場合は、削除 275 if( isRollback || !isOK ) { ConnectionFactory.remove( conn,dbid ); } // 削除 276 else { ConnectionFactory.close( conn,dbid ); } // 返却 277 } 278}