    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.util.Closer;
    019import org.opengion.fukurou.util.ApplicationInfo;
    020
    021import java.sql.Connection;
    022
    023import java.util.Locale;
    024import java.util.Map;
    025import java.util.HashMap;
    026
    027/**
    028 * コネクションを共有して、トランザクションを実現します。
    029 *
    030 * 基本的には、TransactionTag で利用されますが、一部、このオブジェクトを
    031 * 渡して、直接、利用するケースもあります。
    032 *
    033 * トランザクションがすべて完了した後で、realClose() メソッドを呼び出します。
    034 * 一度でも、rollback が指定されていれば、ロールバックを行い、コネクションを
    035 * 破棄します。それ以外で、commit が指定されていれば、コミットを行い、
    036 * コネクションを、プールに戻します。どちらも指定されていなければ、
    037 * コネクションプールに戻すだけになります。
    038 *
    039 * 考え方として、下記のような流れになります。
    040 * <pre>
    041 *   TransactionImpl tran = new TransactionImpl( appInfo ) ;
    042 *   try {
    043 *      ・・・・・
    044 *      tran.commit();
    045 *      tran.finish();
    046 *   }
    047 *   catch( Exception ex ) {
    048 *      tran.rollback();
    049 *   }
    050 *   finally {
    051 *      tran.realClose()
    052 *   }
    053 * </pre>
    054 *
    055 * @og.rev 5.1.9.0 (2010/08/01) 新規作成
    056 *
    057 * @version  5.0
    058 * @author       Kazuhiko Hasegawa
    059 * @since    JDK6.0,
    060 */
    061public class TransactionImpl implements Transaction {
    062        //* このプログラムのVERSION文字列を設定します。   {@value} */
    063        private static final String VERSION = "5.3.8.0 (2011/08/01)" ;
    064
    065        private static final long serialVersionUID = 538020110801L ;
    066
    067        private static final String  DBID = "DEFAULT";
    068        private final ApplicationInfo appInfo ;
    069
    070        private Connection defconn = null;              // 最も利用率の高いDEFAULTだけ、別に変数を用意。
    071
    072        private final Map<String,Connection> dbidMap = new HashMap<String,Connection>();
    073        private boolean isCommit   = false;
    074        private boolean isRollback = false;
    075        private boolean isError    = false;
    076        private boolean isFinish   = false;
    077
    078        /**
    079         * ApplicationInfo を指定して作成する、コンストラクター
    080         *
    081         * このクラスは、基本的には、TransactionTag クラスから作成されます。
    082         *
    083         * @param       appInfo 内部統制用のアクセス情報
    084         */
    085        public TransactionImpl( final ApplicationInfo appInfo ) {
    086                this.appInfo = appInfo ;
    087        }
    088
    089        /**
    090         * 指定のDBID に対応した、Connection オブジェクトを返します。
    091         * 内部Mapに存在していれば、そのコネクションを、存在しなければ、
    092         * 新しく作成します。
    093         *
    094         * @param       dbid  接続先ID
    095         *
    096         * @return      指定のDBID に対応した、Connectionオブジェクト
    097         */
    098        public Connection getConnection( final String dbid ) {
    099                if( dbid == null || dbid.length() == 0 || DBID.equalsIgnoreCase( dbid ) ) {
    100                        if( defconn == null ) {
    101                                defconn = ConnectionFactory.connection( DBID,appInfo );
    102                        }
    103                        return defconn;
    104                }
    105
    106                String udbid = dbid.toUpperCase( Locale.JAPAN );        // 大文字化
    107
    108                Connection conn = dbidMap.get( udbid );
    109                if( conn == null ) {
    110                        conn = ConnectionFactory.connection( udbid,appInfo );
    111                        dbidMap.put( udbid,conn );
    112                }
    113
    114                return conn;
    115        }
    116
    117        /**
    118         * コミット処理が行われた場合に、内部フラグ(isCommit)を true にセットします。
    119         * １回でもコミットが行われており、ロールバックが行われていなければ、
    120         * コミットされます。
    121         *
    122         * 検索処理のみで、エラーが発生していない場合は、コミットも行われないケースがあります。
    123         *
    124         * @return 正常:true/異常:false
    125         */
    126        public boolean commit() {
    127                isCommit = true;
    128                return true;
    129        }
    130
    131        /**
    132         * ロールバック処理が行われた場合に、内部フラグ(isRollback)を true にセットします。
    133         * １回でもロールバックが行われていれば、最終的にはロールバックされます。
    134         *
    135         * ロールバック指定の場合は、isError フラグを true(エラー有)にセットします。
    136         *
    137         * @return 正常:true/異常:false
    138         */
    139        public boolean rollback() {
    140                isRollback = true;
    141                isError    = true;
    142                return true;
    143        }
    144
    145        /**
    146         * トランザクションの、終了時処理を行います。
    147         *
    148         * 実質的には、なにもしません。
    149         *
    150         * @see #close( boolean )
    151         *
    152         * @return 正常:true/異常:false
    153         */
    154        public boolean close() {
    155                return close( false );
    156        }
    157
    158        /**
    159         * トランザクションの、終了時処理を行います。
    160         *
    161         * 引数は、正常かどうかを判定するフラグです。異常の場合は、true をセットします。
    162         * ここでは、実際には何もしませんが、内部的にエラーフラグをセットします。
    163         * (エラーの場合のみセット。リセットはされません)
    164         * 一度でも、エラーが発生したコネクションは、破棄します。それ以外は、プールに戻します。
    165         *
    166         * @param       errFlag         [true:エラー状態/false:通常]
    167         *
    168         * @return 正常:true/異常:false
    169         */
    170        public boolean close( final boolean errFlag ) {
    171                if( errFlag ) { isError = true; }
    172                return true;
    173        }
    174
    175        /**
    176         * トランザクションとして、正常終了時に処理を行います。
    177         *
    178         * 実質的には、内部のfinishフラグをセットするだけです。
    179         * ただし、このフラグがセットされていない場合は、処理が途中で止まった
    180         * 可能性があるため、トランザクションとしては、正常終了させることができません。
    181         *
    182         * @see #realClose()
    183         */
    184        public void finish() {
    185                isFinish = true;
    186        }
    187
    188        /**
    189         * トランザクションとして、終了時処理を行います。
    190         *
    191         * トランザクションがすべて完了した後で、呼び出します。
    192         * 一度でも、Rollback が指定されていれば、ロールバックを行い、コネクションを
    193         * 破棄します。それ以外で、Commit が指定されていれば、コミットを行い、
    194         * コネクションを、プールに戻します。どちらも指定されていなければ、
    195         * コネクションプールに戻すだけになります。
    196         *
    197         * @og.rev 5.3.8.0 (2011/08/01) 内部変数を初期化し、このオブジェクトが再利用できるようにする。
    198         */
    199        public void realClose() {
    200                if( defconn != null ) {
    201                        connClose( defconn,DBID );
    202                }
    203
    204                for( Map.Entry<String,Connection> entry : dbidMap.entrySet() ) {
    205                        String     dbid = entry.getKey();
    206                        Connection conn = entry.getValue();
    207
    208                        connClose( conn,dbid );
    209                }
    210
    211                // 内部変数を初期化します。
    212                defconn    = null;
    213
    214                // 5.3.8.0 (2011/08/01) 内部変数を初期化し、このオブジェクトが再利用できるようにする。
    215                dbidMap.clear();
    216
    217                isCommit   = false;
    218                isRollback = false;
    219                isError    = false;
    220                isFinish   = false;
    221        }
    222
    223        /**
    224         * Connection オブジェクトをクローズします。
    225         *
    226         * 実際にはクローズせず、commit または、rollback を行った後、
    227         * エラー状況に応じて、ConnectionFactory に返却するか、削除します。
    228         * なお、commit または、rollback 時にエラーが発生した場合でも、無視して
    229         * そのまま、処理を継続します。
    230         *
    231         * @param  conn クローズ処理を行う、Connection オブジェクト
    232         * @param  dbid 接続先ID
    233         */
    234        private void connClose( final Connection conn, final String dbid ) {
    235                // まず、コミットかロールバックされた場合は、どちらかの処理が必要
    236                if( isCommit || isRollback ) {
    237                        // commit できる条件：コミットされ、フィニッシュされ、エラーでなく、ロールバックでない
    238                        if( isCommit && isFinish && !isError && !isRollback ) {
    239                                Closer.commit( conn );
    240                        }
    241                        // それ以外は、ロールバックする。
    242                        else {
    243                                Closer.rollback( conn );
    244                        }
    245                }
    246
    247                // 残りは、コミットもロールバックもされていないため、単にキャッシュへの返し方のみ判定する。
    248                // isFinish されていないが、エラーにもなっていない場合は、接続要因以外の問題なので、返却でよい。
    249                if( isError ) { ConnectionFactory.remove( conn,dbid ); }        // 削除
    250                else {                  ConnectionFactory.close( conn,dbid );  }        // 返却
    251
    252        }
    253}