org.ourbeehive.mbp.builder.ResultMapBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.ourbeehive.mbp.builder.ResultMapBuilder.java

Source

/**
 * Copyright (C) 2015-2016 OurBeehive(http://ourbeehive.github.io/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * Project Name: MyBatisPioneer
 * File Name: ResultMapBuilder.java
 * Package Name: org.ourbeehive.mbp.builder
 * 
 * Date: Jan 20, 2016
 * Author: Sericloud
 * 
 */

package org.ourbeehive.mbp.builder;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.ourbeehive.mbp.exception.AppException;
import org.ourbeehive.mbp.lang.JavaSrcElm;
import org.ourbeehive.mbp.model.ant.MapperArtifact;
import org.ourbeehive.mbp.model.ant.MapperProfile;
import org.ourbeehive.mbp.model.ant.RelConfig;
import org.ourbeehive.mbp.model.ant.ResultMapConfig;
import org.ourbeehive.mbp.model.ant.SingleMapperProfile;
import org.ourbeehive.mbp.model.db.OrmColumn;
import org.ourbeehive.mbp.model.db.OrmTable;
import org.ourbeehive.mbp.model.mybatis3.Collection;
import org.ourbeehive.mbp.model.mybatis3.Mapper;
import org.ourbeehive.mbp.model.mybatis3.ObjectFactory;
import org.ourbeehive.mbp.model.mybatis3.Result;
import org.ourbeehive.mbp.model.mybatis3.ResultMap;
import org.ourbeehive.mbp.register.CtxCacheFacade;
import org.ourbeehive.mbp.register.OneToOneIdx;
import org.ourbeehive.mbp.util.ExceptionUtil;
import org.ourbeehive.mbp.util.JavaFormatter;
import org.ourbeehive.mbp.util.MapperFormatter;
import org.ourbeehive.mbp.util.ProfileHelper;

public class ResultMapBuilder {

    private static Logger logger = Logger.getLogger(ResultMapBuilder.class);

    private ObjectFactory mapperObjFactory;
    SelectBuilder selectBuilder;

    public ResultMapBuilder(ObjectFactory mapperObjFactory) {
        this.mapperObjFactory = mapperObjFactory;
        selectBuilder = new SelectBuilder(mapperObjFactory);
    }

    public void buildAllResultMap(Mapper mapper, MapperProfile mapperProfile) throws AppException {

        try {

            // The list of SingleMapperProfile.
            List<SingleMapperProfile> singleMapperProfileList = mapperProfile.getSingleMapperProfile();
            MapperArtifact mapperArtifact = null;
            List<ResultMapConfig> resultMapConfigList = null;
            ResultMapConfig resultMapConfig = null;
            // String className = null;
            String tableName = null;
            String tableAlias = null;

            // TODO 2016.1.22 Pioneer ??single
            /**?for
             * ?single?mapper
             * ????singlenamespacemapper
            **/
            for (SingleMapperProfile singleMapperProfile : singleMapperProfileList) {

                // The list of ResultMapConfig in SingleMapperProfile.
                resultMapConfigList = singleMapperProfile.getResultMapConfig();
                mapperArtifact = singleMapperProfile.getMapperArtifact();

                // Build all resultMap.
                for (int i = 0; i < resultMapConfigList.size(); i++) {

                    // Get class name and table name.
                    resultMapConfig = resultMapConfigList.get(i);
                    // className = resultMapConfig.getClassName();
                    tableName = resultMapConfig.getTableName();
                    tableAlias = resultMapConfig.getTableAlias();

                    if (StringUtils.isNotBlank(tableAlias) == true) {
                        logger.info("RESULTMAP: Handle \"resultMapConfig\" for table \"" + tableName + "("
                                + tableAlias + ").\"");
                    } else {
                        logger.info("RESULTMAP: Handle \"resultMapConfig\" for table \"" + tableName + "\".");
                    }

                    /*
                     * //TODO 2016.1.22 Pioneer ??vo select
                     * ???????singleMapperProfile?
                     * 
                     **/
                    // Map DB table and column into VO class.
                    CtxCacheFacade.addVoClass(mapperProfile, resultMapConfig);

                    // Create 'select' element with id: 'countBySqlClause'.
                    selectBuilder.buildCountRootTableBySql(mapper, mapperProfile, mapperArtifact, resultMapConfig);

                    // Create 'resultMap' element with id: 'select<Root Table Name>ResultMap'.
                    buildRootTableResultMap(mapper, mapperProfile, mapperArtifact, resultMapConfig);

                    // Create 'select' element with id: 'select<Root Table Name>ByPK'.
                    selectBuilder.buildSelectRootTableByPK(mapper, mapperProfile, mapperArtifact, resultMapConfig);

                    // Create 'select' element with id: 'select<Root Table Name>BySqlClause'.
                    selectBuilder.buildSelectRootTableBySql(mapper, mapperProfile, mapperArtifact, resultMapConfig);

                    // Build all oneToOne statements.
                    buildTopLevelJoinStmt(mapper, mapperProfile, mapperArtifact, resultMapConfig);

                }

            }

        } catch (Throwable t) {
            t.printStackTrace();
            ExceptionUtil.handleException(t, logger);
        }

    }

    private void buildRootTableResultMap(Mapper mapper, MapperProfile mapperProfile, MapperArtifact mapperArtifact,
            ResultMapConfig resultMapConfig) throws AppException {

        try {

            // The expected 'id' of 'resultMap'.
            String voSimpleName = JavaFormatter.getVoSimpleName(resultMapConfig, false);
            String resultMapId = MapperFormatter.getResultMapIdOfRootTab(resultMapConfig);

            logger.debug("RESULTMAP: Prepare to build resultMap with id '" + resultMapId + "'.");

            // Lookup 'resultMap' element with full id containing name space.
            ResultMap resultMap = CtxCacheFacade.lookupResultMap(mapperArtifact, resultMapId);
            if (resultMap != null) {
                return;
            }

            // The list of ResultMap.
            List<ResultMap> resultMapList = mapper.getResultMap();

            // Initiate blank 'resultMap' element.
            resultMap = mapperObjFactory.createResultMap();
            resultMapList.add(resultMap);

            // Register the new 'resultMap' element.
            CtxCacheFacade.addResultMap(mapperArtifact, resultMapId, resultMap);

            // Populate the 'id' attribute for resultMap with value '<Root Table Name>ResultMap'.
            resultMap.setId(resultMapId);

            // Populate the 'class' attribute for resultMap.
            String className = resultMapConfig.getClassName();
            resultMap.setType(className);

            populateResultMap(resultMap, mapperProfile, resultMapConfig, null, voSimpleName);

        } catch (Throwable t) {
            ExceptionUtil.handleException(t, logger);
        }

    }

    //
    // <ancesResultMapConfig>
    // ....<descOneToOne>
    // ........<descResultMapConfig>
    // ............<descOneToMany />
    // ........</descResultMapConfig>
    // ....</descOneToOne>
    // ....<descOneToMany />
    // </ancesResultMapConfig>
    //
    private void buildOtoResultMap(Mapper mapper, MapperProfile mapperProfile, MapperArtifact mapperArtifact,
            ResultMapConfig ancesResultMapConfig, List<OneToOneIdx> descOtoIdxList, String resultMapId)
            throws AppException {

        try {

            // The expected 'id' of 'resultMap'.
            if (resultMapId == null) {
                resultMapId = MapperFormatter.getResultMapIdOfMultiTab(ancesResultMapConfig, descOtoIdxList);
            }

            logger.debug("RESULTMAP: Prepare to build resultMap with id '" + resultMapId + "'.");

            // Lookup 'resultMap' element with 'id'.
            ResultMap resultMap = CtxCacheFacade.lookupResultMap(mapperArtifact, resultMapId);
            if (resultMap != null) {
                return;
            }

            // The list of ResultMap.
            List<ResultMap> resultMapList = mapper.getResultMap();

            // Initiate blank 'resultMap' element.
            resultMap = mapperObjFactory.createResultMap();
            resultMapList.add(resultMap);

            // Register the new 'resultMap' element.
            CtxCacheFacade.addResultMap(mapperArtifact, resultMapId, resultMap);

            // Populate the 'id' attribute for resultMap with value '<Root Table Name>ResultMap'.
            resultMap.setId(resultMapId);

            // Populate the 'class' attribute for ResultMap.
            String ancesClassName = ancesResultMapConfig.getClassName();
            resultMap.setType(ancesClassName);

            String voSimpleName = JavaFormatter.getVoSimpleName(ancesResultMapConfig, false);
            populateResultMap(resultMap, mapperProfile, ancesResultMapConfig, null, voSimpleName);

            // If contain one to many association, then claim internal select.
            List<RelConfig> descOtmList = ancesResultMapConfig.getOneToMany();
            if (descOtmList != null && descOtmList.size() != 0) {
                claimIntnlSelect(resultMap, mapperProfile, mapperArtifact, ancesResultMapConfig, null);
            }

            // Prepare to populate 'result' elements for oneToOne association.
            OneToOneIdx descOtoIdx = null;
            String refToSon = null;
            ResultMapConfig descResultMapConfig = null;

            for (int i = 0; i < descOtoIdxList.size(); i++) {

                // Get attributes of every descendant 'resultMapConfig' element.
                descOtoIdx = descOtoIdxList.get(i);
                refToSon = descOtoIdx.getOneToOne().getRefToSon();
                descResultMapConfig = descOtoIdx.getOneToOne().getResultMapConfig();

                // populateResultMap(resultMap, mapperProfile, ancesResultMapConfig, descOtoIdx, descOtoIdx.getAttrNameChain());
                populateResultMap(resultMap, mapperProfile, ancesResultMapConfig, descOtoIdx, refToSon);

                // If contain one to many association, then claim internal
                // select.
                descOtmList = descResultMapConfig.getOneToMany();
                if (descOtmList != null && descOtmList.size() != 0) {

                    logger.debug(
                            "CLAIM INTERNAL SELECT: one to many " + "association exist, claim internal select.");

                    claimIntnlSelect(resultMap, mapperProfile, mapperArtifact, null, descOtoIdx);
                }

            }

        } catch (Throwable t) {
            ExceptionUtil.handleException(t, logger);
        }

    }

    // Inside 'fatherResultMapConfig':
    // <fatherResultMapConfig>
    // ....<sonOneToMany>
    // ........<sonResultMapConfig />
    // ....</sonOneToMany>
    // </fatherResultMapConfig>
    //
    private void buildIntnlOtmResultMap(Mapper mapper, MapperProfile mapperProfile, MapperArtifact mapperArtifact,
            RelConfig sonOtmConfig) throws AppException {

        try {

            // The expected 'id' of 'resultMap'.
            String listOfSon = sonOtmConfig.getListOfSon();
            ResultMapConfig sonResultMapConfig = sonOtmConfig.getResultMapConfig();
            String resultMapId = MapperFormatter.getResultMapIdOfIntnlOtm(mapperProfile, mapperArtifact,
                    sonResultMapConfig, listOfSon);

            logger.debug("RESULTMAP: Prepare to build resultMap with id '" + resultMapId + "'.");

            // Lookup 'resultMap' element with 'id'.
            ResultMap resultMap = CtxCacheFacade.lookupResultMap(mapperArtifact, resultMapId);
            if (resultMap != null) {
                return;
            }

            // The list of ResultMap.
            List<ResultMap> resultMapList = mapper.getResultMap();

            // Initiate blank 'resultMap' element.
            resultMap = mapperObjFactory.createResultMap();
            resultMapList.add(resultMap);

            // Register the new 'resultMap' element.
            CtxCacheFacade.addResultMap(mapperArtifact, resultMapId, resultMap);

            // Populate the 'id' attribute for resultMap with value '<Root Table Name>ResultMap'.
            resultMap.setId(resultMapId);

            // Populate the 'parameterType' attribute for ResultMap.
            String sonClassName = sonResultMapConfig.getClassName();
            resultMap.setType(sonClassName);

            String voSimpleName = JavaFormatter.getVoSimpleName(sonResultMapConfig, false);
            populateResultMap(resultMap, mapperProfile, sonResultMapConfig, null, voSimpleName);

            // Claim internal select.
            claimIntnlSelect(resultMap, mapperProfile, mapperArtifact, sonResultMapConfig, null);

        } catch (Throwable t) {
            ExceptionUtil.handleException(t, logger);
        }

    }

    //
    // Handle 'oneToOne' under top 'resultMapConfig'.
    //
    // From top 'resultMapConfig':
    // <ancesResultMapConfig>
    // ....<descOneToOne> <<--
    // ........<descResultMapConfig>
    // ........</descResultMapConfig>
    // ....</descOneToOne>
    // </ancesResultMapConfig>
    //
    private void buildTopLevelJoinStmt(Mapper mapper, MapperProfile mapperProfile, MapperArtifact mapperArtifact,
            ResultMapConfig ancesResultMapConfig) throws AppException {

        try {

            logger.debug("Execute 'buildTopLevelJoinStmt' from the top 'resultMapConfig'");

            // ??ResultMapConfig?Oto
            // Get all of the oneToOne resultMapConfig.
            List<OneToOneIdx> descOtoIdxList = new ArrayList<OneToOneIdx>();
            computeOtoIndex(descOtoIdxList, ancesResultMapConfig, null, ancesResultMapConfig.getClassName(), null);

            // Oto?Otm
            // Create oneToOne 'resultMap' element, and mark the needed internal oneToMany result map inside the created oneToOne 'resultMap'.
            buildOtoResultMap(mapper, mapperProfile, mapperArtifact, ancesResultMapConfig, descOtoIdxList, null);

            // Otm--------->2016 01 26
            // Build all needed oneToMany statements for these oneToOne statements.
            buildIntnlStmts(mapper, mapperProfile, mapperArtifact, ancesResultMapConfig, descOtoIdxList);

            // Build multi-table select by PK, including oneToOne or oneToMany
            selectBuilder.buildMultiTabSelectByPK(mapper, mapperProfile, mapperArtifact, ancesResultMapConfig,
                    descOtoIdxList);

            // Build multi-table select by SqlClause, including oneToOne or oneToMany.
            selectBuilder.buildMultiTabSelectBySql(mapper, mapperProfile, mapperArtifact, ancesResultMapConfig,
                    descOtoIdxList);

            // Build multi-table select by SqlClause, including oneToOne or oneToMany.
            selectBuilder.buildMultiTabCountBySql(mapper, mapperProfile, mapperArtifact, ancesResultMapConfig,
                    descOtoIdxList);

        } catch (Throwable t) {
            ExceptionUtil.handleException(t, logger);
        }

    }

    //
    // Handle secondary 'oneToOne' under higher 'oneToMany'.
    //
    // From higher 'oneToMany':
    // <fatherResultMapConfig>
    // ....<sonOneToMany>
    // ........<sonResultMapConfig>
    // ............<descOneToOne> <<--
    // ................<descResultMapConfig>
    // ................</descResultMapConfig>
    // ............</descOneToOne>
    // ........</sonResultMapConfig>
    // ........<fatherAttr />
    // ....</sonOneToMany>
    // </fatherResultMapConfig>
    //
    private void buildSecJoinStmt(Mapper mapper, String selectId, String resultMapId, MapperProfile mapperProfile,
            MapperArtifact mapperArtifact, ResultMapConfig fatherResultMapConfig, RelConfig sonOtmConfig)
            throws AppException {

        try {

            logger.debug("Execute 'buildSecJoinStmt' from higher 'oneToMany'.");

            // Get 'resultMapConfig' under higher oneToMany RelConfig.
            ResultMapConfig sonResultMapConfig = sonOtmConfig.getResultMapConfig();
            List<OneToOneIdx> descOtoIdxList = new ArrayList<OneToOneIdx>();
            computeOtoIndex(descOtoIdxList, sonResultMapConfig, null, sonResultMapConfig.getClassName(), null);

            // Create oneToOne 'resultMap' element.
            buildOtoResultMap(mapper, mapperProfile, mapperArtifact, sonResultMapConfig, descOtoIdxList,
                    resultMapId);

            // Build all needed oneToMany statements for these oneToOne statements.
            buildIntnlStmts(mapper, mapperProfile, mapperArtifact, sonResultMapConfig, descOtoIdxList);

            // Build internal oneToOne select with given 'selectId' and 'resultMapId'.
            selectBuilder.buildIntnlSelectOto(mapper, mapperProfile, mapperArtifact, fatherResultMapConfig,
                    sonOtmConfig, descOtoIdxList, selectId, resultMapId);

        } catch (Throwable t) {
            ExceptionUtil.handleException(t, logger);
        }

    }

    // <fatherResultMapConfig>
    // ....<fatherOneToOne>
    // ........<sonResultMapConfig>
    // ............<sonOneToOne />
    // ........</sonResultMapConfig>
    // ....</fatherOneToOne>
    // ....<fatherOneToOne>
    // ........<sonResultMapConfig>
    // ............<sonOneToOne />
    // ........</sonResultMapConfig>
    // ....</fatherOneToOne>
    // </fatherResultMapConfig>
    private void computeOtoIndex(List<OneToOneIdx> otoIndexList, ResultMapConfig fatherResultMapConfig,
            OneToOneIdx ancesOtoIndex, String fullClassNameChain, String simpleClassNameChain) throws AppException {

        try {

            logger.debug("COMPUTE OTO: Begin to compute oneToOne association.");

            String selectStatement = fatherResultMapConfig.getSelectStmt();
            if (StringUtils.isNotBlank(selectStatement) == true) {
                logger.debug("STATEMENT: Compute selectStatement '" + selectStatement + "'");
            }

            OneToOneIdx fatherOtoIdx = null;
            List<RelConfig> fatherOtoList = fatherResultMapConfig.getOneToOne();
            RelConfig fatherOto = null;
            ResultMapConfig sonResultMapConfig = null;
            String sonClassName = null;
            String sonAttrName = null;
            String sonTableName = null;
            String sonTableAlias = null;
            List<RelConfig> sonOtoList = null;
            // If 'fatherResultMapConfig' has more than one oneToOne association, then every one of them has peer.
            boolean hasPeer = fatherOtoList.size() > 1 ? true : false;
            int fatherOtoIdxLoc = otoIndexList.size() - 1;

            for (int i = 0; i < fatherOtoList.size(); i++) {

                // Record the father of current oneToOne association.
                fatherOtoIdx = new OneToOneIdx();
                fatherOtoIdx.setFatherOtoIndex(ancesOtoIndex); // ??
                fatherOtoIdx.setFatherOtoIndexLoc(fatherOtoIdxLoc); // ?Oto

                // Register 'fatherOneToOne' into otoIndexList.
                otoIndexList.add(fatherOtoIdx); // list

                // Set other attributes for 'fatherOtoIdx'.
                fatherOto = fatherOtoList.get(i); // ??ResultMapConfig
                fatherOtoIdx.setOneToOne(fatherOto); // ?
                fatherOtoIdx.setHasPeer(hasPeer); // ?
                // ?[XXDto.]XXvo.XXvo
                if (simpleClassNameChain == null) {
                    sonAttrName = fatherOto.getRefToSon();
                    fatherOtoIdx.setAttrNameChain(sonAttrName);
                } else {
                    sonAttrName = simpleClassNameChain + JavaSrcElm.DOT + fatherOto.getRefToSon();
                    fatherOtoIdx.setAttrNameChain(sonAttrName);
                }
                // ??OtoResultMapConfig
                sonResultMapConfig = fatherOto.getResultMapConfig();
                sonTableName = sonResultMapConfig.getTableName();
                sonTableAlias = sonResultMapConfig.getTableAlias();
                sonClassName = fullClassNameChain + JavaSrcElm.DOT + fatherOto.getRefToSon();
                fatherOtoIdx.setFatherTableName(fatherResultMapConfig.getTableName());
                fatherOtoIdx.setFatherTableAlias(fatherResultMapConfig.getTableAlias());
                fatherOtoIdx.setSonTableName(sonResultMapConfig.getTableName());
                fatherOtoIdx.setSonTableAlias(sonResultMapConfig.getTableAlias());

                // Log the registration of 'fatherOneToOne'.
                if (StringUtils.isNotBlank(sonTableAlias) == true) {
                    logger.debug("REGISTER: Register an OTO association into otoIndexList. " + "className = \""
                            + sonClassName + "\", tableName = \"" + sonTableName + "(" + sonTableAlias + ")\".");
                } else {
                    logger.debug("REGISTER: Register an OTO association into otoIndexList. " + "className = \""
                            + sonClassName + "\", tableName = \"" + sonTableName + "\".");
                }
                logger.debug("JUDGE PEER: Current OTO association has peer '" + hasPeer + "'");

                // ??OtoOto??
                // Get son oneToOne association.
                sonOtoList = sonResultMapConfig.getOneToOne();

                // If having son oneToOne association.
                if (sonOtoList != null && sonOtoList.size() != 0) {
                    // Invoke recursively to find all descendant oneToOne associations
                    computeOtoIndex(otoIndexList, sonResultMapConfig, fatherOtoIdx, sonClassName, sonAttrName);
                }

            }

        } catch (Throwable t) {
            ExceptionUtil.handleException(t, logger);
        }

    }

    //
    // <ancesResultMapConfig>
    // ....<descOneToOne />
    // </ancesResultMapConfig>
    //
    private void populateResultMap(ResultMap resultMap, MapperProfile mapperProfile,
            ResultMapConfig ancesResultMapConfig, OneToOneIdx descOneToOneIdx, String attrNameChain)
            throws AppException {

        try {

            // If the given oneToOne is not null, then consider it as current resultMapConfig.
            // String ancesClassName = ancesResultMapConfig.getClassName();
            RelConfig descOneToOne = null;
            ResultMapConfig currResultMapConfig = null;
            // String refToSon = null;

            if (descOneToOneIdx != null) {
                descOneToOne = descOneToOneIdx.getOneToOne();
                currResultMapConfig = descOneToOne.getResultMapConfig();
                // refToSon = descOneToOne.getRefToSon();
            } else {
                currResultMapConfig = ancesResultMapConfig;
            }

            String tableName = currResultMapConfig.getTableName();
            String tableAlias = currResultMapConfig.getTableAlias();

            // Find OrmTable according to the table name.
            OrmTable ormTable = CtxCacheFacade.lookupOrmTable(tableName);
            if (ormTable == null) {
                logger.error("!!! NO TABLE !!!: No table found with table name: " + tableName);
                return;
            }

            List<Result> resultList = resultMap.getResult();
            Result result = null;

            OrmColumn ormColumn = null;
            String columnName = null;
            String attrName = null;
            // OrmAttr ormAttr = null;

            logger.debug("ATTR NAME CHAIN: The given attrNameChain is: " + attrNameChain);

            if (attrNameChain == null) {
                attrNameChain = "";
            } else {
                attrNameChain = attrNameChain + JavaSrcElm.DOT;
            }

            HashSet<String> includedAttrs = ProfileHelper.getIncludedAttrName(currResultMapConfig);
            HashSet<String> excludedAttrs = ProfileHelper.getExcludedAttrName(currResultMapConfig);

            // Populate 'result' elements according to the the ClassEntity definition.
            List<OrmColumn> ormColumnList = ormTable.getColumnList();
            for (int i = 0; i < ormColumnList.size(); i++) {

                // Check inclusion and exclusion, inclusion take higher preference.
                ormColumn = ormColumnList.get(i);
                columnName = ormColumn.getName();

                // Translate column name to java attribute name.
                attrName = JavaFormatter.getJavaStyle(columnName, false);

                if (includedAttrs.size() != 0) {
                    if (includedAttrs.contains(attrName) == false) {
                        logger.debug("EXCLUDE ATTRIBUTE: Property '" + attrName
                                + "' is NOT in the inclusion list, skipped.");
                        continue;
                    }
                } else if (excludedAttrs.size() != 0) {
                    if (excludedAttrs.contains(attrName) == true) {
                        logger.debug("EXCLUDE ATTRIBUTE: Property '" + attrName
                                + "' is in the exclusion list, skipped.");
                        continue;
                    }
                }

                result = mapperObjFactory.createResult();
                result.setProperty(attrNameChain + attrName);
                result.setColumn(MapperFormatter.getColumnAlias(mapperProfile, tableName, tableAlias, columnName));
                result.setJdbcType(ormColumn.getJdbcTypeName());
                resultList.add(result);

                logger.debug("FIND MAPPING: Find mapping between attribute '" + attrNameChain + attrName
                        + "' and column '" + columnName + "'");

            }

        } catch (Throwable t) {
            ExceptionUtil.handleException(t, logger);
        }

    }

    //
    // Get attributes from 'fatherResultMapConfig' or 'fatherOtoIdx'.
    //
    // From 'fatherResultMapConfig':
    // <fatherResultMapConfig>
    // ....<sonOneToMany />
    // </fatherResultMapConfig>
    //
    // From 'fatherOtoIdx':
    // <fatherOneToOne>
    // ....<fatherResultMapConfig />
    // ........<sonOneToMany />
    // </fatherOneToOne>
    //
    private void claimIntnlSelect(ResultMap resultMap, MapperProfile mapperProfile, MapperArtifact mapperArtifact,
            ResultMapConfig fatherResultMapConfig, OneToOneIdx fatherOtoIdx) throws AppException {

        try {

            String fatherTableName = null;
            String fatherTableAlias = null;
            List<RelConfig> sonOneToManyList = null;

            // If the given 'fatherResultMapConfig' is not null, then get attributes from it.
            if (fatherResultMapConfig != null) {
                fatherTableName = fatherResultMapConfig.getTableName();
                fatherTableAlias = fatherResultMapConfig.getTableAlias();
                sonOneToManyList = fatherResultMapConfig.getOneToMany();
            }
            // If the given 'fatherResultMapConfig' is null, then get attributes from 'fatherOtoIdx'.
            else if (fatherOtoIdx != null) {
                RelConfig fatherOneToOne = fatherOtoIdx.getOneToOne();
                fatherResultMapConfig = fatherOneToOne.getResultMapConfig();
                fatherTableName = fatherResultMapConfig.getTableName();
                fatherTableAlias = fatherResultMapConfig.getTableAlias();
                sonOneToManyList = fatherResultMapConfig.getOneToMany();
            } else {
                throw new IllegalArgumentException(
                        "'ancesResultMapConfig' and 'otoIndex' " + "could not be null simultaneously.");
            }

            // Find OrmTable according to the table name.
            OrmTable fatherTable = CtxCacheFacade.lookupOrmTable(fatherTableName);
            if (fatherTable == null) {
                logger.error("!!! NO TABLE !!!: No table found with table name: " + fatherTableName);
                return;
            }

            // Prepare to claim internal select for every oneToMany association.
            RelConfig sonOneToMany = null;
            ResultMapConfig sonResultMapConfig = null;
            String listOfSon = null;
            String sonClassName = null;
            String fatherAttrName = null;
            String fatherColumnName = null;
            OrmColumn fatherColumn = null;
            // String attrAlias = null;
            String columnAlias = null;

            List<Collection> collectionList = resultMap.getCollection();
            Collection collection = null;
            // Result result = null;

            for (int i = 0; i < sonOneToManyList.size(); i++) {

                sonOneToMany = sonOneToManyList.get(i);
                sonResultMapConfig = sonOneToMany.getResultMapConfig();
                sonClassName = sonResultMapConfig.getClassName();
                listOfSon = sonOneToMany.getListOfSon();
                fatherAttrName = sonOneToMany.getFatherAttr();
                fatherColumnName = JavaFormatter.getDbStyle(fatherAttrName);
                fatherColumn = fatherTable.getColumnIdx().get(fatherColumnName);

                if (fatherColumn != null) {

                    collection = mapperObjFactory.createCollection();
                    columnAlias = MapperFormatter.getColumnAlias(mapperProfile, fatherTableName, fatherTableAlias,
                            fatherColumnName);
                    collection.setProperty(listOfSon);
                    collection.setOfType(sonClassName);
                    collection.setColumn(columnAlias);
                    collection.setSelect(MapperFormatter.getSelIdOfIntnlOtm(mapperProfile, mapperArtifact,
                            sonResultMapConfig, listOfSon, false));
                    collectionList.add(collection);

                    logger.info("FIND MAPPING: Find mapping between attribute '" + listOfSon + "' and column '"
                            + columnAlias + "'");

                }

            }

        } catch (Throwable t) {
            ExceptionUtil.handleException(t, logger);
        }

    }

    //
    // Handle 'oneToMany' directly under 'fatherResultMapConfig', and those under 'sonOtoIndexList'.
    //
    // Inside 'fatherResultMapConfig':
    // <fatherResultMapConfig>
    // ....<sonOneToMany>
    // ........<sonResultMapConfig>
    // ............<descOneToOne />
    // ............<descOneToMany>
    // ................<descResultMapConfig />
    // ...........</descOneToMany>
    // ........</sonResultMapConfig>
    // ....</sonOneToMany>
    // ....<sonOneToOne>
    // ........<sonResultMapConfig>
    // ............<descOneToMany>
    // ................<descResultMapConfig>
    // ....................<descOneToOne />
    // ................</descResultMapConfig>
    // ...........</descOneToMany>
    // ........</sonResultMapConfig>
    // ....</sonOneToOne>
    // ....<sonOneToOne />
    // </fatherResultMapConfig>
    //
    private void buildIntnlStmts(Mapper mapper, MapperProfile mapperProfile, MapperArtifact mapperArtifact,
            ResultMapConfig fatherResultMapConfig, List<OneToOneIdx> sonOtoIndexList) throws AppException {

        try {
            //add base
            CtxCacheFacade.addDtoAttr(mapperProfile, mapperArtifact, fatherResultMapConfig.getClassName(),
                    fatherResultMapConfig.getTableName(), null, null, false, null);
            // Handle 'oneToMany' directly under 'fatherResultMapConfig'.
            List<RelConfig> sonOneToManyList = fatherResultMapConfig.getOneToMany();
            RelConfig sonOneToMany = null;
            String listOfSon = null;
            ResultMapConfig sonResultMapConfig = null;
            List<RelConfig> descOneToOneList = null;
            List<RelConfig> descOneToManyList = null;

            String selectId = null;
            String resultMapId = null;

            if (sonOneToManyList != null) {

                for (int i = 0; i < sonOneToManyList.size(); i++) {

                    // If there is oneToOne association under 'sonResultMapConfig', then handle them.
                    sonOneToMany = sonOneToManyList.get(i);
                    sonResultMapConfig = sonOneToMany.getResultMapConfig();
                    descOneToOneList = sonResultMapConfig.getOneToOne();
                    descOneToManyList = sonResultMapConfig.getOneToMany();

                    //TODO add oneToMany list attr to dto 
                    CtxCacheFacade.addDtoAttr(mapperProfile, mapperArtifact, fatherResultMapConfig.getClassName(),
                            fatherResultMapConfig.getTableName(), sonOneToMany.getListOfSon(),
                            sonResultMapConfig.getTableName(), true, sonResultMapConfig.getClassName());

                    if (descOneToOneList != null && descOneToOneList.size() != 0) {

                        logger.debug(
                                "OTO in OTM: Found OTO in OTM, need build addtional " + "one to one statements.");

                        // Use the mandatory 'id' for private 'select'.
                        listOfSon = sonOneToMany.getListOfSon();
                        selectId = MapperFormatter.getSelIdOfIntnlOtm(mapperProfile, mapperArtifact,
                                sonResultMapConfig, listOfSon, false);

                        // Use the mandatory 'id' for private 'resultMap'.
                        resultMapId = MapperFormatter.getResultMapIdOfIntnlOtm(mapperProfile, mapperArtifact,
                                sonResultMapConfig, listOfSon);
                        buildSecJoinStmt(mapper, selectId, resultMapId, mapperProfile, mapperArtifact,
                                fatherResultMapConfig, sonOneToMany);

                    }
                    // If there is oneToMany association under 'sonResultMapConfig', then handle them.
                    else if (descOneToManyList != null && descOneToManyList.size() != 0) {

                        logger.debug(
                                "OTM in OTM: Found OTM in OTM, need build addtional " + "one to many statements.");

                        buildIntnlOtmResultMap(mapper, mapperProfile, mapperArtifact, sonOneToMany);
                        selectBuilder.buildIntnlSelectOneTable(mapper, mapperProfile, mapperArtifact, null, null,
                                fatherResultMapConfig, sonOneToMany);

                        buildIntnlStmts(mapper, mapperProfile, mapperArtifact, sonResultMapConfig,
                                new ArrayList<OneToOneIdx>());

                    }
                    // If there is neither oneToOne nor oneToMany association under 'sonResultMapConfig'.
                    else {

                        buildIntnlOtmResultMap(mapper, mapperProfile, mapperArtifact, sonOneToMany);
                        selectBuilder.buildIntnlSelectOneTable(mapper, mapperProfile, mapperArtifact, null, null,
                                fatherResultMapConfig, sonOneToMany);

                    }

                }

            }

            // Handle all of 'oneToMany' inside 'sonOtoIndexList'.
            OneToOneIdx sonOtoIndex = null;
            RelConfig sonOneToOne = null;
            // OrmClass sonOrmClass = null;
            RelConfig descOneToMany = null;
            ResultMapConfig descResultMapConfig = null;
            if (sonOtoIndexList != null) {

                for (int i = 0; i < sonOtoIndexList.size(); i++) {

                    sonOtoIndex = sonOtoIndexList.get(i);
                    sonOneToOne = sonOtoIndex.getOneToOne();
                    sonResultMapConfig = sonOneToOne.getResultMapConfig();
                    descOneToManyList = sonResultMapConfig.getOneToMany();

                    //TODO add oneToOne attr to dto
                    CtxCacheFacade.addDtoAttr(mapperProfile, mapperArtifact, fatherResultMapConfig.getClassName(),
                            fatherResultMapConfig.getTableName(), sonOneToOne.getRefToSon(),
                            sonResultMapConfig.getTableName(), false, sonResultMapConfig.getClassName());

                    // Because current 'sonResultMapConfig' is defined inside oneToOne association, so its 'className' attribute is null and should
                    // be set.
                    // sonOrmClass = CtxCacheFacade.getOrmClass(fatherResultMapConfig.getClassName(), sonOtoIndex);
                    // sonResultMapConfig.setClassName(sonOrmClass.getFullName());

                    if (descOneToManyList != null) {

                        for (int j = 0; j < descOneToManyList.size(); j++) {

                            descOneToMany = descOneToManyList.get(j);

                            // If there is oneToOne association inside 'descOneToMany', then handle oneToOne firstly.
                            descResultMapConfig = descOneToMany.getResultMapConfig();
                            descOneToOneList = descResultMapConfig.getOneToOne();

                            //TODO add oneTo many attr to Dto
                            CtxCacheFacade.addDtoAttr(mapperProfile, mapperArtifact,
                                    fatherResultMapConfig.getClassName(), fatherResultMapConfig.getTableName(),
                                    descOneToMany.getListOfSon(), descResultMapConfig.getTableName(), true,
                                    descResultMapConfig.getClassName());

                            if (descOneToOneList != null && descOneToOneList.size() != 0) {

                                logger.debug("OTO in OTM: Found OTO in OTM, need build addtional "
                                        + "one to one statements.");

                                // Use the mandatory 'id' for private 'select'.
                                listOfSon = descOneToMany.getListOfSon();
                                selectId = MapperFormatter.getSelIdOfIntnlOtm(mapperProfile, mapperArtifact,
                                        descResultMapConfig, listOfSon, false);

                                // Use the mandatory 'id' for oneToOneResultMap.
                                resultMapId = MapperFormatter.getResultMapIdOfIntnlOtm(mapperProfile,
                                        mapperArtifact, descResultMapConfig, listOfSon);
                                buildSecJoinStmt(mapper, selectId, resultMapId, mapperProfile, mapperArtifact,
                                        sonResultMapConfig, descOneToMany);

                            } else {

                                buildIntnlOtmResultMap(mapper, mapperProfile, mapperArtifact, descOneToMany);
                                selectBuilder.buildIntnlSelectOneTable(mapper, mapperProfile, mapperArtifact,
                                        fatherResultMapConfig, sonOtoIndex, null, descOneToMany);

                            }

                        }

                    }

                }

            }

        } catch (Throwable t) {
            ExceptionUtil.handleException(t, logger);
        }

    }

    // private void cacheClassConst(MapperProfile mapperProfile, String ancesClassName, OneToOneIdx descOtoIdx, String tableName, String tableAlias)
    // throws AppException {
    //
    // try {
    //
    // String dtoDomainName = getClassDomainName(ancesClassName);
    // String constantName = null;
    // if (descOtoIdx != null) {
    // String attrNameChain = descOtoIdx.getAttrNameChain();
    // attrNameChain = StringUtils.replace(attrNameChain, JavaSrcElm.DOT, JavaSrcElm.UNDER_LINE);
    // constantName = dtoDomainName + JavaSrcElm.UNDER_LINE + attrNameChain;
    // } else {
    // constantName = dtoDomainName;
    // }
    //
    // String constantValue = null;
    // if (StringUtils.isNotBlank(tableAlias) == true) {
    // constantValue = tableAlias;
    // } else {
    // constantValue = MapperFormatter.getTableShortName(tableName);
    // }
    //
    // logger.debug("CACHE CONST: " + "Prepare to cache '" + constantName + " = " + constantValue + "'");
    //
    // // Get settings for ClassConstant.
    // AllMapperProfile allMapperProfile = mapperProfile.getAllMapperProfile();
    // ComnArtifact comnArtifact = allMapperProfile.getComnArtifact();
    // // String constPkg = comnArtifact.getConstPkg();
    // String classFullName = comnArtifact.getClassConst();
    //
    // ClassAnalyzer classAnalyzer = new ClassAnalyzer();
    // classAnalyzer.cacheConst(classFullName, constantName, constantValue);
    //
    // } catch (Throwable t) {
    // throw new AppException(t);
    // }
    //
    // }

    // private void cacheAttrConst(MapperProfile mapperProfile, String propName, String columnName) throws AppException {
    //
    // try {
    //
    // // logger.debug("CACHE CONST: Prepare to cache '" + propName + " = "
    // // + columnName + "'");
    //
    // // Get settings for ClassConstant.
    // AllMapperProfile allMapperProfile = mapperProfile.getAllMapperProfile();
    // ComnArtifact comnArtifact = allMapperProfile.getComnArtifact();
    // String classFullName = comnArtifact.getAttrConst();
    //
    // ClassAnalyzer classAnalyzer = new ClassAnalyzer();
    // classAnalyzer.cacheConst(classFullName, propName, columnName);
    //
    // } catch (Throwable t) {
    // throw new AppException(t);
    // }
    //
    // }

    // private String getClassDomainName(String classFullName) {
    //
    // int rightFirst = classFullName.lastIndexOf(JavaSrcElm.DOT);
    // String dtoPkg = classFullName.substring(0, rightFirst);
    // String classSimpleName = classFullName.substring(rightFirst + 1, classFullName.length());
    //
    // // Skip 'dto' in package name.
    // int rightSecond = dtoPkg.lastIndexOf(JavaSrcElm.DOT);
    // String domainPkg = classFullName.substring(0, rightSecond);
    // int rightThird = domainPkg.lastIndexOf(JavaSrcElm.DOT);
    // String domainName = domainPkg.substring(rightThird + 1, domainPkg.length());
    //
    // // Return domainName and shortName.
    // return domainName + JavaSrcElm.UNDER_LINE + classSimpleName;
    //
    // }

}