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 * コミット処理が行われた場合に、内部フラグ(isCommit)を true にセットします。 144 * 1回でもコミットが行われており、ロールバックが行われていなければ、 145 * コミットされます。 146 * 147 * 検索処理時でも、最後に commit() を実行してください。実行されていない場合は、 148 * 自動的に、rollback() が、実行されます。 149 * 150 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。return 不要。 151 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 152 * 153 */ 154 @Override 155 public void commit() { 156 isCommit = true; 157 } 158 159 /** 160 * ロールバック処理が行われた場合に、内部フラグ(isRollback)を true にセットします。 161 * 1回でもロールバックが行われていれば、最終的にはロールバックされます。 162 * 163 * 一度も、ロールバックが行われていない場合でも、コミットが行われていない場合は、 164 * rollback()を実行します。 165 * 166 * 167 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。return 不要。 168 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 169 * 170 */ 171 @Override 172 public void rollback() { 173 isRollback = true; 174 } 175 176 /** 177 * トランザクションの、終了時処理を行います。 178 * 179 * それまでの処理は、すべて正常に処理できた場合に、使用します。 180 * 181 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。return 不要。 182 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 183 * 184 * @see AutoCloseable#close() 185 */ 186 @Override 187 public void close() { 188 // Impl は、finish() で、実質的な完了処理を行う。 189 } 190 191 /** 192 * 最終的なコミットが行われた場合に、内部フラグ(isEndCommit)を true にセットします。 193 * 通常は、この処理は、1値度だけ実行されます。 194 * 初期値が、false なので、途中で一度でも実行されると、true にセットされ、最後まで 195 * 処理されたとみなされてしまうので、注意してください。 196 * 197 * 通常は、タグリブの、doEndTag() が実行された場合に、呼びます。 198 * このフラグが、true でないと(つまり、一度でも呼ばれないと)最終的に、commit されません。 199 * 200 * なお、endCommit() が呼ばれると、自動的に、commit() も呼んでおきます。 201 * 202 * @og.rev 6.4.3.3 (2016/03/04) 一般的なタグで、SKIP_PAGE された場合、rollback するようにします。 203 */ 204 public void endCommit() { 205 isEndCommit = true ; 206 isCommit = true ; 207 } 208 209 /** 210 * トランザクションとして、終了時処理を行います。 211 * 212 * トランザクションがすべて完了した後で、呼び出します。 213 * 一度でも、Rollback が指定されていれば、ロールバックを行い、コネクションを 214 * 破棄します。それ以外で、Commit が指定されていれば、コミットを行い、 215 * コネクションを、プールに戻します。どちらも指定されていなければ、 216 * コネクションプールに戻すだけになります。 217 * 218 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。メソッド名変更。 219 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 220 * @og.rev 6.4.3.4 (2016/03/11) Map#computeIfAbsent で対応する。 221 */ 222 public void finish() { 223 if( isFinish ) { 224 final String errMsg = "すでに、finish() 実行済みです。" + CR 225 + " 新規に Transaction オブジェクトの作成から行ってください。" + CR ; 226 throw new OgRuntimeException( errMsg ); 227 } 228 229 if( defconn != null ) { 230 connClose( defconn,DBID ); 231 defconn = null; // 内部変数を初期化します。 232 } 233 234 // Map#computeIfAbsent : 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし 235 dbidMap.forEach( (dbid,conn) -> connClose( conn,dbid ) ); 236 dbidMap.clear(); 237 238 isFinish = true; // 次回実行時にエラーにします。 239 } 240 241 /** 242 * Connection オブジェクトをクローズします。 243 * 244 * 245 * commit されており、rollback されていない場合のみ、commit します。 246 * それ以外は、rollback します。 247 * rollback された場合は、コネクションプールに戻さずに、破棄します。 248 * 本来は、その、Connection に問題はないかもしれませんが、あえて、 249 * キャッシュを継続させる必要もないと考えます。 250 * 251 * @og.rev 6.3.6.1 (2015/08/28) AutoCloseable の close() メソッドに対応。内部処理見直し。 252 * @og.rev 6.3.9.0 (2015/11/06) Use block level rather than method level synchronization.(PMD) 253 * @og.rev 6.4.3.3 (2016/03/04) 一般的なタグで、SKIP_PAGE された場合、rollback するようにします。 254 * 255 * @param conn クローズ処理を行う、Connectionオブジェクト 256 * @param dbid 接続先ID 257 */ 258 private void connClose( final Connection conn, final String dbid ) { 259 // commit されており、rollback されていない場合 260 final boolean isOK ; 261 if( isCommit && isEndCommit && !isRollback ) { // 6.4.3.3 (2016/03/04) 262 isOK = Closer.commit( conn ); 263 } 264 else { 265 isOK = Closer.rollback( conn ); 266 } 267 268 // rollback されているか、commit/rollback でエラーが発生した場合は、削除 269 if( isRollback || !isOK ) { ConnectionFactory.remove( conn,dbid ); } // 削除 270 else { ConnectionFactory.close( conn,dbid ); } // 返却 271 } 272}