org.seasar.dbflute.s2dao.extension.TnRowCreatorExtension.java Source code

Java tutorial

Introduction

Here is the source code for org.seasar.dbflute.s2dao.extension.TnRowCreatorExtension.java

Source

/*
 * Copyright 2004-2011 the Seasar Foundation and the Others.
 *
 * 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.
 */
package org.seasar.dbflute.s2dao.extension;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.seasar.dbflute.Entity;
import org.seasar.dbflute.dbmeta.DBMeta;
import org.seasar.dbflute.dbmeta.info.ColumnInfo;
import org.seasar.dbflute.exception.MappingClassCastException;
import org.seasar.dbflute.jdbc.ValueType;
import org.seasar.dbflute.resource.DBFluteSystem;
import org.seasar.dbflute.resource.InternalMapContext;
import org.seasar.dbflute.resource.ResourceContext;
import org.seasar.dbflute.s2dao.metadata.TnPropertyMapping;
import org.seasar.dbflute.s2dao.rowcreator.impl.TnRowCreatorImpl;
import org.seasar.dbflute.util.DfTypeUtil;

/**
 * @author jflute
 */
public class TnRowCreatorExtension extends TnRowCreatorImpl {

    // ===================================================================================
    //                                                                          Definition
    //                                                                          ==========
    /** Log instance. */
    private static final Log _log = LogFactory.getLog(TnRowCreatorExtension.class);

    /** The key of DBMeta cache. */
    protected static final String DBMETA_CACHE_KEY = "df:DBMetaCache";

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    protected DBMeta _fixedDBMeta;
    protected boolean _creatableByDBMeta;

    // ===================================================================================
    //                                                                         Constructor
    //                                                                         ===========
    protected TnRowCreatorExtension() {
    }

    /**
     * @param beanClass The class of target bean to find DB-meta. (NullAllowed)
     * @return The instance of internal row creator. (NotNull)
     */
    public static TnRowCreatorExtension createRowCreator(Class<?> beanClass) {
        final TnRowCreatorExtension rowCreator = new TnRowCreatorExtension();
        if (beanClass != null) {
            final DBMeta dbmeta = findDBMetaByClass(beanClass);
            if (dbmeta != null) {
                rowCreator.setFixedDBMeta(dbmeta);
                rowCreator.setCreatableByDBMeta(isCreatableByDBMeta(beanClass, dbmeta.getEntityType()));
            }
        }
        return rowCreator;
    }

    protected static DBMeta findDBMetaByClass(Class<?> beanClass) {
        if (!Entity.class.isAssignableFrom(beanClass)) {
            return null;
        }
        // getting from entity because the bean may be customize entity.
        final Object instance = newInstance(beanClass); // only when initialization
        return ((Entity) instance).getDBMeta();
    }

    protected static Object newInstance(Class<?> clazz) {
        try {
            return clazz.newInstance();
        } catch (InstantiationException e) {
            throw new IllegalStateException(e);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    protected static boolean isCreatableByDBMeta(Class<?> beanClass, Class<?> entityType) {
        // Returns false when the bean is not related to the entity or is a sub class of the entity.
        return beanClass.isAssignableFrom(entityType);
    }

    // ===================================================================================
    //                                                                                Main
    //                                                                                ====
    /**
     * {@inheritDoc}
     */
    public Object createRow(ResultSet rs, Map<String, TnPropertyMapping> propertyCache, Class<?> beanClass)
            throws SQLException {
        if (propertyCache.isEmpty()) {
            String msg = "The propertyCache should not be empty: bean=" + beanClass.getName();
            throw new IllegalStateException(msg);
        }
        String columnName = null;
        TnPropertyMapping mapping = null;
        String propertyName = null;
        Object selectedValue = null;
        final Map<String, Integer> selectIndexMap = ResourceContext.getSelectIndexMap();
        final Object row;
        final DBMeta dbmeta;
        if (_fixedDBMeta != null) {
            if (_creatableByDBMeta) {
                row = _fixedDBMeta.newEntity();
            } else {
                row = newBean(beanClass);
            }
            dbmeta = _fixedDBMeta;
        } else {
            row = newBean(beanClass);
            dbmeta = findCachedDBMeta(row);
        }
        try {
            if (dbmeta != null) {
                final Set<Entry<String, TnPropertyMapping>> entrySet = propertyCache.entrySet();
                for (Entry<String, TnPropertyMapping> entry : entrySet) {
                    columnName = entry.getKey();
                    mapping = entry.getValue();
                    propertyName = mapping.getPropertyName();
                    selectedValue = getValue(rs, columnName, mapping.getValueType(), selectIndexMap);
                    final ColumnInfo columnInfo = mapping.getEntityColumnInfo();
                    if (columnInfo != null) {
                        columnInfo.write((Entity) row, selectedValue);
                    } else {
                        mapping.getPropertyAccessor().setValue(row, selectedValue);
                    }
                }
            } else {
                final Set<Entry<String, TnPropertyMapping>> entrySet = propertyCache.entrySet();
                for (Entry<String, TnPropertyMapping> entry : entrySet) {
                    columnName = entry.getKey();
                    mapping = entry.getValue();
                    propertyName = mapping.getPropertyName();
                    selectedValue = getValue(rs, columnName, mapping.getValueType(), selectIndexMap);
                    mapping.getPropertyAccessor().setValue(row, selectedValue);
                }
            }
            return row;
        } catch (ClassCastException e) {
            throwMappingClassCastException(row, dbmeta, mapping, selectedValue, e);
            return null; // unreachable
        } catch (SQLException e) {
            if (_log.isDebugEnabled()) {
                String msg = "Failed to get selected values while resultSet handling:";
                msg = msg + " target=" + DfTypeUtil.toClassTitle(beanClass) + "." + propertyName;
                _log.debug(msg);
            }
            throw e;
        }
    }

    protected Object getValue(ResultSet rs, String columnName, ValueType valueType,
            Map<String, Integer> selectIndexMap) throws SQLException {
        final Object value;
        if (selectIndexMap != null) {
            value = ResourceContext.getValue(rs, columnName, valueType, selectIndexMap);
        } else {
            value = valueType.getValue(rs, columnName);
        }
        return value;
    }

    protected void throwMappingClassCastException(Object entity, DBMeta dbmeta, TnPropertyMapping mapping,
            Object selectedValue, ClassCastException e) {
        String msg = "Look! Read the message below." + ln();
        msg = msg + "/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *" + ln();
        msg = msg + "Failed to cast a class while data mapping!" + ln();
        msg = msg + ln();
        msg = msg + "[Advice]" + ln();
        msg = msg + "If you use Seasar(S2Container), this exception may be" + ln();
        msg = msg + "from ClassLoader Headache about HotDeploy." + ln();
        msg = msg + "Add the ignore-package setting to convention.dicon like this:" + ln();
        msg = msg + "  For example:" + ln();
        msg = msg + "    <initMethod name=?addIgnorePackageName?>" + ln();
        msg = msg + "        <arg>?com.example.xxx.dbflute?</arg>" + ln();
        msg = msg + "    </initMethod>" + ln();
        msg = msg + "If you use an other DI container, this exception may be" + ln();
        msg = msg + "from illegal state about your settings of DBFlute." + ln();
        msg = msg + "Confirm your settings: for example, typeMappingMap.dfprop." + ln();
        msg = msg + ln();
        msg = msg + "[Exception Message]" + ln() + e.getMessage() + ln();
        msg = msg + ln();
        msg = msg + "[Target Entity]" + ln() + entity + ln();
        msg = msg + "classLoader: " + entity.getClass().getClassLoader() + ln();
        msg = msg + ln();
        msg = msg + "[Target DBMeta]" + ln() + dbmeta + ln();
        msg = msg + "classLoader: " + dbmeta.getClass().getClassLoader() + ln();
        msg = msg + ln();
        msg = msg + "[Property Mapping]" + ln() + mapping + ln();
        msg = msg + "type: " + (mapping != null ? mapping.getClass() : null) + ln();
        msg = msg + ln();
        msg = msg + "[Selected Value]" + ln() + selectedValue + ln();
        msg = msg + "type: " + (selectedValue != null ? selectedValue.getClass() : null) + ln();
        msg = msg + "* * * * * * * * * */";
        throw new MappingClassCastException(msg, e);
    }

    // ===================================================================================
    //                                                                       Assist Helper
    //                                                                       =============
    /**
     * @param row The instance of row. (NotNull)
     * @return The interface of DBMeta. (NullAllowed: If it's null, it means NotFound.)
     */
    public static DBMeta findCachedDBMeta(Object row) {
        return DBMetaCacheHandler.findDBMeta(row);
    }

    /**
     * @param rowType The type of row. (NotNull)
     * @param tableName The name of table. (NotNull)
     * @return The interface of DBMeta. (NullAllowed: If it's null, it means NotFound.)
     */
    public static DBMeta findCachedDBMeta(Class<?> rowType, String tableName) {
        return DBMetaCacheHandler.findDBMeta(rowType, tableName);
    }

    protected static class DBMetaCacheHandler {

        /** The key of DBMeta cache. */
        protected static final String DBMETA_CACHE_KEY = "df:DBMetaCache";

        public static DBMeta findDBMeta(Object row) {
            if (!(row instanceof Entity)) {
                return null;
            }
            final Entity entity = (Entity) row;
            DBMeta dbmeta = getCachedDBMeta(entity.getClass());
            if (dbmeta != null) {
                return dbmeta;
            }
            dbmeta = entity.getDBMeta();
            cacheDBMeta(entity, dbmeta);
            return dbmeta;
        }

        public static DBMeta findDBMeta(Class<?> rowType, String tableName) {
            DBMeta dbmeta = getCachedDBMeta(rowType);
            if (dbmeta != null) {
                return dbmeta;
            }
            // No check because the table name is not always for domain.
            dbmeta = ResourceContext.provideDBMeta(tableName);
            cacheDBMeta(rowType, dbmeta);
            return dbmeta;
        }

        protected static DBMeta getCachedDBMeta(Class<?> rowType) {
            Map<Class<?>, DBMeta> contextCacheMap = getDBMetaContextCacheMap();
            if (contextCacheMap == null) {
                contextCacheMap = new HashMap<Class<?>, DBMeta>();
                InternalMapContext.setObject(DBMETA_CACHE_KEY, contextCacheMap);
            }
            return contextCacheMap.get(rowType);
        }

        protected static void cacheDBMeta(Entity entity, DBMeta dbmeta) {
            cacheDBMeta(entity.getClass(), dbmeta);
        }

        protected static void cacheDBMeta(Class<?> type, DBMeta dbmeta) {
            final Map<Class<?>, DBMeta> dbmetaCache = getDBMetaContextCacheMap();
            dbmetaCache.put(type, dbmeta);
        }

        @SuppressWarnings("unchecked")
        protected static Map<Class<?>, DBMeta> getDBMetaContextCacheMap() {
            return (Map<Class<?>, DBMeta>) InternalMapContext.getObject(DBMETA_CACHE_KEY);
        }
    }

    // ===================================================================================
    //                                                                      General Helper
    //                                                                      ==============
    protected String ln() {
        return DBFluteSystem.getBasicLn();
    }

    // ===================================================================================
    //                                                                            Accessor
    //                                                                            ========
    public void setFixedDBMeta(DBMeta fixedDBMeta) {
        this._fixedDBMeta = fixedDBMeta;
    }

    public void setCreatableByDBMeta(boolean creatableByDBMeta) {
        this._creatableByDBMeta = creatableByDBMeta;
    }
}