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