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

com.landawn.abacus.metadata.sql.SQLTable Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2015 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.metadata.sql;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Element;

import com.landawn.abacus.core.NameUtil;
import com.landawn.abacus.exception.UncheckedSQLException;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.metadata.Column;
import com.landawn.abacus.metadata.EntityDefXmlEle.EntityDefEle.EntityEle.TableEle;
import com.landawn.abacus.metadata.EntityDefXmlEle.EntityDefEle.EntityEle.TableEle.ColumnEle;
import com.landawn.abacus.metadata.Table;
import com.landawn.abacus.util.Configuration;
import com.landawn.abacus.util.ImmutableMap;
import com.landawn.abacus.util.JdbcUtil;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.WD;
import com.landawn.abacus.util.XMLUtil;

// TODO: Auto-generated Javadoc
/**
 *
 * @author Haiyang Li
 * @since 0.8
 */
public class SQLTable implements Table {

    protected static final Logger logger = LoggerFactory.getLogger(SQLTable.class);

    private static final String COLUMN_NAME = "COLUMN_NAME";

    private static final String PRIMARY_KEY_GOT_SQL = "SELECT cols.column_name FROM all_constraints cons, all_cons_columns cols WHERE cols.table_name = ? AND cons.constraint_type = 'P' AND cons.constraint_name = cols.constraint_name";

    private static final String ORACLE = "ORACLE";

    private static final String NUMBER = "NUMBER";

    private static final String SMALLINT = "SMALLINT";

    private static final String TINYINT = "TINYINT";

    private final String name;

    private final Map attrs;

    private final Map columnMap;

    private final Map columnPool = new HashMap<>();

    public SQLTable(String name, Connection conn) {
        this(parse(name, conn));
    }

    public SQLTable(InputStream is) {
        this(Configuration.parse(is).getDocumentElement());
    }

    public SQLTable(Element tableNode) {
        this(parse(tableNode));
    }

    SQLTable(Object[] attrsColumnMap) {
        this((Map) attrsColumnMap[0], (Map) attrsColumnMap[1]);
    }

    SQLTable(Map attrs, Map columnMap) {
        this.name = NameUtil.getCachedName(attrs.get(TableEle.NAME));
        attrs.put(TableEle.NAME, name);
        this.attrs = ImmutableMap.of(attrs);

        for (SQLColumn column : columnMap.values()) {
            column.setTable(this);

            columnPool.put(column.getName(), column);
            columnPool.put(column.getCanonicalName(), column);
        }

        this.columnMap = ImmutableMap.of(columnMap);
    }

    /**
     * Gets the name.
     *
     * @return
     */
    @Override
    public String getName() {
        return name;
    }

    /**
     * Gets the column list.
     *
     * @return
     */
    @SuppressWarnings("rawtypes")
    @Override
    public Collection getColumnList() {
        return (Collection) columnMap.values();
    }

    /**
     * Gets the column name list.
     *
     * @return
     */
    @Override
    public Collection getColumnNameList() {
        return columnMap.keySet();
    }

    /**
     * Gets the column.
     *
     * @param columnName
     * @return
     */
    @Override
    public Column getColumn(String columnName) {
        return columnPool.get(columnName);
    }

    /**
     * Gets the attributes.
     *
     * @return
     */
    @Override
    public Map getAttributes() {
        return attrs;
    }

    /**
     * Gets the attribute.
     *
     * @param attrName
     * @return
     */
    @Override
    public String getAttribute(String attrName) {
        return attrs.get(attrName);
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    /**
     *
     * @param obj
     * @return true, if successful
     */
    @Override
    public boolean equals(Object obj) {
        return this == obj || (obj instanceof SQLTable && ((SQLTable) obj).name.equals(name));
    }

    @Override
    public String toString() {
        return attrs.toString();
    }

    /**
     * Column type 2 java type.
     *
     * @param columnTypeName
     * @param columnClassName
     * @param precision
     * @param scale
     * @return
     */
    public static String columnType2JavaType(String columnTypeName, String columnClassName, int precision, int scale) {
        String javaType = null;

        if (NUMBER.equalsIgnoreCase(columnTypeName)) {
            if (scale == 0) {
                if (precision == 1) {
                    javaType = Boolean.class.getCanonicalName();
                } else if (precision <= 3) {
                    javaType = Byte.class.getCanonicalName();
                } else if (precision <= 5) {
                    javaType = Short.class.getCanonicalName();
                } else if (precision <= 10) {
                    javaType = Integer.class.getCanonicalName();
                } else if (precision <= 19) {
                    javaType = Long.class.getCanonicalName();
                } else {
                    javaType = columnClassName;
                }
            } else {
                if ((precision <= 39) && (Math.abs(scale) <= 53)) {
                    javaType = Float.class.getCanonicalName();
                } else if ((precision <= 79) && (Math.abs(scale) <= 107)) {
                    javaType = Double.class.getCanonicalName();
                } else {
                    javaType = columnClassName;
                }
            }
        } else if (TINYINT.equalsIgnoreCase(columnTypeName) && (precision <= 3)) {
            javaType = Byte.class.getCanonicalName();
        } else if (SMALLINT.equalsIgnoreCase(columnTypeName) && (precision <= 5)) {
            javaType = Short.class.getCanonicalName();
        } else {
            javaType = columnClassName.equals("[B") ? "byte[]" : columnClassName;
        }

        return javaType;
    }

    /**
     *
     * @param tableName
     * @param conn
     * @return
     */
    private static Object[] parse(String tableName, Connection conn) {
        final Map columnMap = new LinkedHashMap<>();

        PreparedStatement stmt = null;
        ResultSet rs = null;

        List primaryKeys = new ArrayList<>();

        try {
            DatabaseMetaData dmd = conn.getMetaData();

            if (dmd.getDatabaseProductName().equalsIgnoreCase(ORACLE)) {
                stmt = conn.prepareStatement(PRIMARY_KEY_GOT_SQL);
                stmt.setString(1, tableName);
                rs = stmt.executeQuery();
            } else {
                rs = dmd.getPrimaryKeys(null, null, tableName);
            }

            while (rs.next()) {
                primaryKeys.add(rs.getString(COLUMN_NAME));
            }
        } catch (SQLException e) {
            // ignore
            logger.error("Failed to get primary keys for table : " + tableName, e);
        } finally {
            JdbcUtil.closeQuietly(rs, stmt);
        }

        final String sql = "SELECT * FROM " + tableName + " WHERE 1 > 2";

        try {
            stmt = conn.prepareStatement(sql);
            rs = stmt.executeQuery();

            ResultSetMetaData rmd = rs.getMetaData();

            for (int i = 1; i <= rmd.getColumnCount(); i++) {
                final Map attrs = new LinkedHashMap<>();

                final String columnName = rmd.getColumnName(i);
                final String columnTypeName = rmd.getColumnTypeName(i);
                final String columnClassName = rmd.getColumnClassName(i);
                final int precision = rmd.getPrecision(i);
                final int scale = rmd.getScale(i);

                final String javaType = columnType2JavaType(columnTypeName, columnClassName, precision, scale);

                String jdbcType = columnTypeName;
                if (precision > 0 && Math.abs(scale) > 0) {
                    jdbcType += (WD.PARENTHESES_L + precision + WD.COMMA_SPACE + scale + WD.PARENTHESES_R);
                } else if (precision > 0) {
                    jdbcType += (WD.PARENTHESES_L + precision + WD.PARENTHESES_R);
                }

                attrs.put(ColumnEle.NAME, columnName);
                attrs.put(ColumnEle.JAVA_TYPE, javaType);
                attrs.put(ColumnEle.JDBC_TYPE, jdbcType);
                attrs.put(ColumnEle.SQL_TYPE, String.valueOf(rmd.getColumnType(i)));

                if (primaryKeys.contains(columnName)) {
                    attrs.put(ColumnEle.IS_PRIMARY_KEY, String.valueOf(true));
                    attrs.put(ColumnEle.IS_UNIQUE, String.valueOf(true));
                }

                attrs.put(ColumnEle.IS_AUTO_INCREMENT, String.valueOf(rmd.isAutoIncrement(i)));

                // some vendor jdbc doesn't support below property.
                try {
                    attrs.put(ColumnEle.IS_READ_ONLY, N.toString(rmd.isReadOnly(i)));
                } catch (Exception e) {
                    attrs.put(ColumnEle.IS_READ_ONLY, "false");
                }

                try {
                    attrs.put(ColumnEle.IS_WRITABLE, N.toString(rmd.isWritable(i)));
                } catch (Exception e) {
                    attrs.put(ColumnEle.IS_WRITABLE, "true");
                }

                int nullability = rmd.isNullable(i);

                if (ResultSetMetaData.columnNullable == nullability) {
                    attrs.put(ColumnEle.IS_NULLABLE, String.valueOf(true));
                } else if (ResultSetMetaData.columnNoNulls == nullability) {
                    attrs.put(ColumnEle.IS_NULLABLE, String.valueOf(false));
                } else {
                    // if unknown, set it to false.
                    attrs.put(ColumnEle.IS_NULLABLE, String.valueOf(false));
                }

                try {
                    attrs.put(ColumnEle.IS_SEARCHABLE, N.toString(rmd.isSearchable(i)));
                } catch (Exception e) {
                    attrs.put(ColumnEle.IS_SEARCHABLE, "true");
                }

                attrs.put(ColumnEle.IS_CASE_SENSITIVE, String.valueOf(rmd.isCaseSensitive(i)));

                SQLColumn column = new SQLColumn(attrs, tableName);

                columnMap.put(column.getName(), column);
            }
        } catch (SQLException e) {
            throw new UncheckedSQLException(e);
        } finally {
            JdbcUtil.closeQuietly(rs, stmt, null);
        }

        final Map attrs = N.asLinkedHashMap(TableEle.NAME, tableName);

        return new Object[] { attrs, columnMap };
    }

    /**
     *
     * @param tableNode
     * @return
     */
    private static Object[] parse(Element tableNode) {
        final String tableName = tableNode.getAttribute(TableEle.NAME);
        final Map attrs = XMLUtil.readAttributes(tableNode);
        final Map columnMap = new LinkedHashMap<>();

        final List columnElementList = XMLUtil.getElementsByTagName(tableNode, ColumnEle.COLUMN);
        for (Element columnElement : columnElementList) {
            SQLColumn column = new SQLColumn(XMLUtil.readAttributes(columnElement), tableName);
            columnMap.put(column.getName(), column);
        }

        return new Object[] { attrs, columnMap };
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy