All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.landawn.abacus.util.QueryUtil Maven / Gradle / Ivy

There is a newer version: 2.1.7
Show newest version
/*
 * Copyright (C) 2021 HaiYang Li
 *
 * 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 com.landawn.abacus.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.annotation.Immutable;
import com.landawn.abacus.annotation.Internal;
import com.landawn.abacus.annotation.NotColumn;
import com.landawn.abacus.annotation.Table;
import com.landawn.abacus.parser.ParserUtil;
import com.landawn.abacus.parser.ParserUtil.BeanInfo;
import com.landawn.abacus.parser.ParserUtil.PropInfo;
import com.landawn.abacus.type.Type;
import com.landawn.abacus.util.Tuple.Tuple2;

public final class QueryUtil {

    private QueryUtil() {
        // singleton
    }

    private static final Map, ImmutableMap> column2PropNameNameMapPool = new ConcurrentHashMap<>();

    private static final Map, Map>> entityTablePropColumnNameMap = new ObjectPool<>(N.POOL_SIZE);

    private static final Map, Map>>> entityTablePropColumnNameMap2 = new ObjectPool<>(
            N.POOL_SIZE);

    /**
     *
     * @param entityClass
     * @param namingPolicy
     * @return
     * @deprecated for internal use only.
     */
    @Deprecated
    @Beta
    public static ImmutableMap> prop2ColumnNameMap(final Class entityClass, final NamingPolicy namingPolicy) {
        Map>> namingPropColumnNameMap = entityTablePropColumnNameMap2.get(entityClass);
        ImmutableMap> result = null;

        if (namingPropColumnNameMap == null || (result = namingPropColumnNameMap.get(namingPolicy)) == null) {
            final ImmutableMap prop2ColumnNameMap = getProp2ColumnNameMap(entityClass, namingPolicy);
            final Map> newProp2ColumnNameMap = N.newHashMap(prop2ColumnNameMap.size() * 2);

            for (final Map.Entry entry : prop2ColumnNameMap.entrySet()) {
                newProp2ColumnNameMap.put(entry.getKey(), Tuple.of(entry.getValue(), entry.getKey().indexOf('.') < 0));

                if (!prop2ColumnNameMap.containsKey(entry.getValue())) {
                    newProp2ColumnNameMap.put(entry.getValue(), Tuple.of(entry.getValue(), entry.getValue().indexOf('.') < 0));
                }
            }

            result = ImmutableMap.wrap(newProp2ColumnNameMap);

            if (namingPropColumnNameMap == null) {
                namingPropColumnNameMap = new EnumMap<>(NamingPolicy.class);
                // TODO not necessary?
                // namingPropColumnNameMap = Collections.synchronizedMap(namingPropColumnNameMap)
                entityTablePropColumnNameMap2.put(entityClass, namingPropColumnNameMap);
            }

            namingPropColumnNameMap.put(namingPolicy, result);
        }

        return result;
    }

    /**
     * Gets the column 2 field name map.
     *
     * @param entityClass
     * @return
     */
    public static ImmutableMap getColumn2PropNameMap(final Class entityClass) {
        ImmutableMap result = column2PropNameNameMapPool.get(entityClass);

        if (result == null) {
            final BeanInfo entityInfo = ParserUtil.getBeanInfo(entityClass);
            final Map map = N.newHashMap(entityInfo.propInfoList.size() * 3);

            for (final PropInfo propInfo : entityInfo.propInfoList) {
                if (propInfo.columnName.isPresent()) {
                    map.put(propInfo.columnName.get(), propInfo.name);
                    map.put(propInfo.columnName.get().toLowerCase(), propInfo.name);
                    map.put(propInfo.columnName.get().toUpperCase(), propInfo.name);
                }
            }

            result = ImmutableMap.copyOf(map);

            column2PropNameNameMapPool.put(entityClass, result);
        }

        return result;
    }

    /**
     * Gets the prop column name map.
     *
     * @param entityClass
     * @param namingPolicy
     * @return
     */
    public static ImmutableMap getProp2ColumnNameMap(final Class entityClass, final NamingPolicy namingPolicy) {
        if (entityClass == null || Map.class.isAssignableFrom(entityClass)) {
            return ImmutableMap.empty();
        }

        final Map> namingColumnNameMap = entityTablePropColumnNameMap.get(entityClass);
        ImmutableMap result = null;

        if (namingColumnNameMap == null || (result = namingColumnNameMap.get(namingPolicy)) == null) {
            result = registerEntityPropColumnNameMap(entityClass, namingPolicy, null);
        }

        return result;
    }

    static ImmutableMap registerEntityPropColumnNameMap(final Class entityClass, final NamingPolicy namingPolicy,
            final Set> registeringClasses) {
        N.checkArgNotNull(entityClass);

        if (registeringClasses != null) {
            if (registeringClasses.contains(entityClass)) {
                throw new RuntimeException("Cycling references found among: " + registeringClasses);
            }

            registeringClasses.add(entityClass);
        }

        final Table tableAnno = entityClass.getAnnotation(Table.class);
        final Set columnFields = tableAnno == null ? N.emptySet() : N.asSet(tableAnno.columnFields());
        final Set nonColumnFields = tableAnno == null ? N.emptySet() : N.asSet(tableAnno.nonColumnFields());
        final BeanInfo entityInfo = ParserUtil.getBeanInfo(entityClass);
        Map propColumnNameMap = N.newHashMap(entityInfo.propInfoList.size() * 2);

        for (final PropInfo propInfo : entityInfo.propInfoList) {
            if (isNotColumn(columnFields, nonColumnFields, propInfo)) {
                continue;
            }

            if (propInfo.columnName.isPresent()) {
                propColumnNameMap.put(propInfo.name, propInfo.columnName.get());
            } else {
                propColumnNameMap.put(propInfo.name, SQLBuilder.formalizeColumnName(propInfo.name, namingPolicy));

                final Type propType = propInfo.type.isCollection() ? propInfo.type.getElementType() : propInfo.type;

                if (propType.isBean() && (registeringClasses == null || !registeringClasses.contains(propType.clazz()))) {
                    final Set> newRegisteringClasses = registeringClasses == null ? N.> newLinkedHashSet() : registeringClasses;
                    newRegisteringClasses.add(entityClass);

                    final Map subPropColumnNameMap = registerEntityPropColumnNameMap(propType.clazz(), namingPolicy, newRegisteringClasses);

                    if (N.notEmpty(subPropColumnNameMap)) {
                        final String subTableAliasOrName = SQLBuilder.getTableAliasOrName(propType.clazz(), namingPolicy);

                        for (final Map.Entry entry : subPropColumnNameMap.entrySet()) {
                            propColumnNameMap.put(propInfo.name + WD.PERIOD + entry.getKey(), subTableAliasOrName + WD.PERIOD + entry.getValue());
                        }

                        propColumnNameMap.remove(propInfo.name); // remove sub entity prop.
                    }
                }
            }
        }

        //    final Map tmp = entityTablePropColumnNameMap.get(entityClass);
        //
        //    if (N.notEmpty(tmp)) {
        //        propColumnNameMap.putAll(tmp);
        //    }

        if (N.isEmpty(propColumnNameMap)) {
            propColumnNameMap = N. emptyMap();
        }

        final ImmutableMap result = ImmutableMap.wrap(propColumnNameMap);

        Map> namingPropColumnMap = entityTablePropColumnNameMap.get(entityClass);

        if (namingPropColumnMap == null) {
            namingPropColumnMap = new EnumMap<>(NamingPolicy.class);
            // TODO not necessary?
            // namingPropColumnMap = Collections.synchronizedMap(namingPropColumnMap)
            entityTablePropColumnNameMap.put(entityClass, namingPropColumnMap);
        }

        namingPropColumnMap.put(namingPolicy, result);

        return result;
    }

    /**
     * Gets the insert prop names by class.
     *
     * @param entity
     * @param excludedPropNames
     * @return
     */
    @Internal
    public static Collection getInsertPropNames(final Object entity, final Set excludedPropNames) {
        final Class entityClass = entity.getClass();

        final Collection[] val = SQLBuilder.loadPropNamesByClass(entityClass);

        if (!N.isEmpty(excludedPropNames)) {
            final List tmp = new ArrayList<>(val[2]);
            tmp.removeAll(excludedPropNames);
            return tmp;
        }

        final Collection idPropNames = getIdFieldNames(entityClass);

        if (N.isEmpty(idPropNames)) {
            return val[2];
        }

        final BeanInfo entityInfo = ParserUtil.getBeanInfo(entityClass);

        for (final String idPropName : idPropNames) {
            if (!SQLBuilder.isDefaultIdPropValue(entityInfo.getPropInfo(idPropName))) {
                return val[2];
            }
        }

        return val[3];
    }

    /**
     * Gets the insert prop names by class.
     *
     * @param entityClass
     * @param excludedPropNames
     * @return
     */
    @Internal
    public static Collection getInsertPropNames(final Class entityClass, final Set excludedPropNames) {
        final Collection[] val = SQLBuilder.loadPropNamesByClass(entityClass);
        final Collection propNames = val[2];

        if (N.isEmpty(excludedPropNames)) {
            return propNames;
        }
        final List tmp = new ArrayList<>(propNames);
        tmp.removeAll(excludedPropNames);
        return tmp;
    }

    /**
     * Gets the select prop names by class.
     *
     * @param entityClass
     * @param includeSubEntityProperties
     * @param excludedPropNames
     * @return
     */
    @Internal
    public static Collection getSelectPropNames(final Class entityClass, final boolean includeSubEntityProperties,
            final Set excludedPropNames) {
        final Collection[] val = SQLBuilder.loadPropNamesByClass(entityClass);
        final Collection propNames = includeSubEntityProperties ? val[0] : val[1];

        if (N.isEmpty(excludedPropNames)) {
            return propNames;
        }

        return N.excludeAll(propNames, excludedPropNames);
    }

    /**
     * Gets the update prop names by class.
     *
     * @param entityClass
     * @param excludedPropNames
     * @return
     */
    @Internal
    public static Collection getUpdatePropNames(final Class entityClass, final Set excludedPropNames) {
        final Collection[] val = SQLBuilder.loadPropNamesByClass(entityClass);
        final Collection propNames = val[4];

        if (N.isEmpty(excludedPropNames)) {
            return propNames;
        }
        final List tmp = new ArrayList<>(propNames);
        tmp.removeAll(excludedPropNames);
        return tmp;
    }

    /**
     * Gets the id field names.
     *
     * @param targetClass
     * @return an immutable List.
     * @deprecated for internal only.
     */
    @Deprecated
    @Internal
    @Immutable
    public static List getIdFieldNames(final Class targetClass) {
        return getIdFieldNames(targetClass, false);
    }

    /**
     * Gets the id field names.
     *
     * @param targetClass
     * @param fakeIdForEmpty
     * @return an immutable List.
     * @deprecated for internal only.
     */
    @Deprecated
    @Internal
    @Immutable
    public static List getIdFieldNames(final Class targetClass, final boolean fakeIdForEmpty) {
        final ImmutableList idPropNames = ParserUtil.getBeanInfo(targetClass).idPropNameList;

        return N.isEmpty(idPropNames) && fakeIdForEmpty ? fakeIds : idPropNames;
    }

    /**
     *
     *
     * @param columnFields
     * @param nonColumnFields
     * @param propInfo
     * @return
     */
    public static boolean isNotColumn(final Set columnFields, final Set nonColumnFields, final PropInfo propInfo) {
        return propInfo.isTransient || propInfo.isAnnotationPresent(NotColumn.class) || (N.notEmpty(columnFields) && !columnFields.contains(propInfo.name))
                || (N.notEmpty(nonColumnFields) && nonColumnFields.contains(propInfo.name));
    }

    private static final ImmutableList fakeIds = ImmutableList.of("not_defined_fake_id_in_abacus_" + Strings.uuid());

    /**
     * Checks if is fake id.
     *
     * @param idPropNames
     * @return true, if is fake id
     * @deprecated for internal only.
     */
    @Deprecated
    @Internal
    public static boolean isFakeId(final List idPropNames) {
        return idPropNames != null && idPropNames.size() == 1 && fakeIds.get(0).equals(idPropNames.get(0));
    }

    private static final Map QM_CACHE = new HashMap<>();

    static {
        for (int i = 0; i <= 30; i++) {
            QM_CACHE.put(i, Strings.repeat("?", i, ", "));
        }

        QM_CACHE.put(100, Strings.repeat("?", 100, ", "));
        QM_CACHE.put(200, Strings.repeat("?", 200, ", "));
        QM_CACHE.put(300, Strings.repeat("?", 300, ", "));
        QM_CACHE.put(500, Strings.repeat("?", 500, ", "));
        QM_CACHE.put(1000, Strings.repeat("?", 1000, ", "));
    }

    /**
     * Repeat question mark({@code ?}) {@code n} times with delimiter {@code ", "}.
     * 
* It's designed for batch SQL builder. * * @param n * @return */ public static String repeatQM(final int n) { N.checkArgNotNegative(n, "count"); String result = QM_CACHE.get(n); if (result == null) { result = Strings.repeat("?", n, ", "); } return result; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy