This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch past-M2 in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 55d99a6da256a1a0a1bae69be51e967e0365f45b Author: Andrus Adamchik <[email protected]> AuthorDate: Sun Jun 14 15:32:36 2026 -0400 DbAttribute: getJavaClass() -> getJavaClassName() to address the assymetry with ObjAttribute.getJavaClass() that returns a Class --- .../apache/cayenne/access/jdbc/BatchAction.java | 2 +- .../cayenne/access/jdbc/ColumnDescriptor.java | 2 +- .../translator/select/ResultNodeDescriptor.java | 2 +- .../java/org/apache/cayenne/dba/TypesMapping.java | 1052 ++++++++++---------- .../java/org/apache/cayenne/map/DbAttribute.java | 2 +- 5 files changed, 531 insertions(+), 529 deletions(-) diff --git a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java index ada634f2a..5b7319800 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java @@ -308,6 +308,6 @@ public class BatchAction extends BaseSQLAction { return attributeForDbAttribute.getType(); } } - return key.getJavaClass(); + return key.getJavaClassName(); } } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java index 74d7c09d9..90ad64ae5 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ColumnDescriptor.java @@ -91,7 +91,7 @@ public class ColumnDescriptor { this.attribute = attribute; this.namePrefix = tableAlias; - this.javaClass = attribute.getJavaClass(); + this.javaClass = attribute.getJavaClassName(); if (attribute.getEntity() != null) { this.tableName = attribute.getEntity().getName(); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/ResultNodeDescriptor.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/ResultNodeDescriptor.java index 280a0000f..1f03335b8 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/ResultNodeDescriptor.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/ResultNodeDescriptor.java @@ -106,7 +106,7 @@ class ResultNodeDescriptor { return property.getType().getCanonicalName(); } if (getDbAttribute() != null) { - return getDbAttribute().getJavaClass(); + return getDbAttribute().getJavaClassName(); } return null; } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/TypesMapping.java b/cayenne/src/main/java/org/apache/cayenne/dba/TypesMapping.java index 82c3c5e0c..3f1b62f31 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/TypesMapping.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/TypesMapping.java @@ -73,530 +73,532 @@ import static java.sql.Types.VARCHAR; */ public class TypesMapping { - // Never use "-1" or any other normal integer, since there - // is a big chance it is being reserved in java.sql.Types - public static final int NOT_DEFINED = Integer.MAX_VALUE; - - // char constants for the sql data types - public static final String SQL_ARRAY = "ARRAY"; - public static final String SQL_BIGINT = "BIGINT"; - public static final String SQL_BINARY = "BINARY"; - public static final String SQL_BIT = "BIT"; - public static final String SQL_BLOB = "BLOB"; - - /** - * @since 1.2 - */ - public static final String SQL_BOOLEAN = "BOOLEAN"; - - public static final String SQL_CLOB = "CLOB"; - public static final String SQL_NCLOB = "NCLOB"; - public static final String SQL_CHAR = "CHAR"; - public static final String SQL_NCHAR = "NCHAR"; - public static final String SQL_DATE = "DATE"; - public static final String SQL_DECIMAL = "DECIMAL"; - public static final String SQL_DOUBLE = "DOUBLE"; - public static final String SQL_FLOAT = "FLOAT"; - public static final String SQL_INTEGER = "INTEGER"; - public static final String SQL_LONGVARCHAR = "LONGVARCHAR"; - public static final String SQL_LONGNVARCHAR = "LONGNVARCHAR"; - public static final String SQL_LONGVARBINARY = "LONGVARBINARY"; - public static final String SQL_NUMERIC = "NUMERIC"; - public static final String SQL_REAL = "REAL"; - public static final String SQL_SMALLINT = "SMALLINT"; - public static final String SQL_TINYINT = "TINYINT"; - public static final String SQL_TIME = "TIME"; - public static final String SQL_TIMESTAMP = "TIMESTAMP"; - public static final String SQL_VARBINARY = "VARBINARY"; - public static final String SQL_VARCHAR = "VARCHAR"; - public static final String SQL_NVARCHAR = "NVARCHAR"; - public static final String SQL_SQLXML = "SQLXML"; - public static final String SQL_OTHER = "OTHER"; - public static final String SQL_NULL = "NULL"; - - // char constants for Java data types - public static final String JAVA_LONG = "java.lang.Long"; - public static final String JAVA_BYTES = "byte[]"; - public static final String JAVA_BOOLEAN = "java.lang.Boolean"; - public static final String JAVA_STRING = "java.lang.String"; - public static final String JAVA_SQLDATE = "java.sql.Date"; - public static final String JAVA_UTILDATE = "java.util.Date"; - public static final String JAVA_BIGDECIMAL = "java.math.BigDecimal"; - public static final String JAVA_BIGINTEGER = "java.math.BigInteger"; - public static final String JAVA_DOUBLE = "java.lang.Double"; - public static final String JAVA_FLOAT = "java.lang.Float"; - public static final String JAVA_INTEGER = "java.lang.Integer"; - public static final String JAVA_SHORT = "java.lang.Short"; - public static final String JAVA_BYTE = "java.lang.Byte"; - public static final String JAVA_TIME = "java.sql.Time"; - public static final String JAVA_TIMESTAMP = "java.sql.Timestamp"; - public static final String JAVA_BLOB = "java.sql.Blob"; - - /** - * Keys: SQL string type names, Values: SQL int type definitions from - * java.sql.Types - */ - private static final Map<String, Integer> SQL_STRING_TYPE = new HashMap<>(); - - /** - * Keys: SQL int type definitions from java.sql.Types, Values: SQL string - * type names - */ - private static final Map<Integer, String> SQL_ENUM_TYPE = new HashMap<>(); - - /** - * Keys: SQL int type definitions from java.sql.Types, Values: java class - * names - */ - private static final Map<Integer, String> SQL_ENUM_JAVA = new HashMap<>(); - - /** - * Keys: java class names, Values: SQL int type definitions from - * java.sql.Types - */ - private static final Map<String, Integer> JAVA_SQL_ENUM = new HashMap<>(); - - static { - // SQL_STRING_TYPE.put(SQL_ARRAY, ARRAY); - SQL_STRING_TYPE.put(SQL_BIGINT, BIGINT); - SQL_STRING_TYPE.put(SQL_BINARY, BINARY); - SQL_STRING_TYPE.put(SQL_BIT, BIT); - SQL_STRING_TYPE.put(SQL_BLOB, BLOB); - SQL_STRING_TYPE.put(SQL_BOOLEAN, BOOLEAN); - SQL_STRING_TYPE.put(SQL_CLOB, CLOB); - SQL_STRING_TYPE.put(SQL_NCLOB, NCLOB); - SQL_STRING_TYPE.put(SQL_CHAR, CHAR); - SQL_STRING_TYPE.put(SQL_NCHAR, NCHAR); - SQL_STRING_TYPE.put(SQL_DATE, DATE); - SQL_STRING_TYPE.put(SQL_DECIMAL, DECIMAL); - SQL_STRING_TYPE.put(SQL_DOUBLE, DOUBLE); - SQL_STRING_TYPE.put(SQL_FLOAT, FLOAT); - SQL_STRING_TYPE.put(SQL_INTEGER, INTEGER); - SQL_STRING_TYPE.put(SQL_LONGVARCHAR, LONGVARCHAR); - SQL_STRING_TYPE.put(SQL_LONGNVARCHAR, LONGNVARCHAR); - SQL_STRING_TYPE.put(SQL_LONGVARBINARY, LONGVARBINARY); - SQL_STRING_TYPE.put(SQL_NUMERIC, NUMERIC); - SQL_STRING_TYPE.put(SQL_REAL, REAL); - SQL_STRING_TYPE.put(SQL_SMALLINT, SMALLINT); - SQL_STRING_TYPE.put(SQL_TINYINT, TINYINT); - SQL_STRING_TYPE.put(SQL_TIME, TIME); - SQL_STRING_TYPE.put(SQL_TIMESTAMP, TIMESTAMP); - SQL_STRING_TYPE.put(SQL_VARBINARY, VARBINARY); - SQL_STRING_TYPE.put(SQL_VARCHAR, VARCHAR); - SQL_STRING_TYPE.put(SQL_NVARCHAR, NVARCHAR); - SQL_STRING_TYPE.put(SQL_OTHER, OTHER); - SQL_STRING_TYPE.put(SQL_NULL, NULL); - - SQL_ENUM_TYPE.put(ARRAY, SQL_ARRAY); - SQL_ENUM_TYPE.put(BIGINT, SQL_BIGINT); - SQL_ENUM_TYPE.put(BINARY, SQL_BINARY); - SQL_ENUM_TYPE.put(BIT, SQL_BIT); - SQL_ENUM_TYPE.put(BOOLEAN, SQL_BOOLEAN); - SQL_ENUM_TYPE.put(BLOB, SQL_BLOB); - SQL_ENUM_TYPE.put(CLOB, SQL_CLOB); - SQL_ENUM_TYPE.put(NCLOB, SQL_NCLOB); - SQL_ENUM_TYPE.put(CHAR, SQL_CHAR); - SQL_ENUM_TYPE.put(NCHAR, SQL_NCHAR); - SQL_ENUM_TYPE.put(DATE, SQL_DATE); - SQL_ENUM_TYPE.put(DECIMAL, SQL_DECIMAL); - SQL_ENUM_TYPE.put(DOUBLE, SQL_DOUBLE); - SQL_ENUM_TYPE.put(FLOAT, SQL_FLOAT); - SQL_ENUM_TYPE.put(INTEGER, SQL_INTEGER); - SQL_ENUM_TYPE.put(LONGVARCHAR, SQL_LONGVARCHAR); - SQL_ENUM_TYPE.put(LONGNVARCHAR, SQL_LONGNVARCHAR); - SQL_ENUM_TYPE.put(LONGVARBINARY, SQL_LONGVARBINARY); - SQL_ENUM_TYPE.put(NUMERIC, SQL_NUMERIC); - SQL_ENUM_TYPE.put(REAL, SQL_REAL); - SQL_ENUM_TYPE.put(SMALLINT, SQL_SMALLINT); - SQL_ENUM_TYPE.put(TINYINT, SQL_TINYINT); - SQL_ENUM_TYPE.put(TIME, SQL_TIME); - SQL_ENUM_TYPE.put(TIMESTAMP, SQL_TIMESTAMP); - SQL_ENUM_TYPE.put(VARBINARY, SQL_VARBINARY); - SQL_ENUM_TYPE.put(VARCHAR, SQL_VARCHAR); - SQL_ENUM_TYPE.put(NVARCHAR, SQL_NVARCHAR); - SQL_ENUM_TYPE.put(SQLXML, SQL_SQLXML); - SQL_ENUM_TYPE.put(OTHER, SQL_OTHER); - SQL_ENUM_TYPE.put(NULL, SQL_NULL); - - SQL_ENUM_JAVA.put(BIGINT, JAVA_LONG); - SQL_ENUM_JAVA.put(BINARY, JAVA_BYTES); - SQL_ENUM_JAVA.put(BIT, JAVA_BOOLEAN); - SQL_ENUM_JAVA.put(BOOLEAN, JAVA_BOOLEAN); - SQL_ENUM_JAVA.put(BLOB, JAVA_BYTES); - SQL_ENUM_JAVA.put(CLOB, JAVA_STRING); - SQL_ENUM_JAVA.put(NCLOB, JAVA_STRING); - SQL_ENUM_JAVA.put(CHAR, JAVA_STRING); - SQL_ENUM_JAVA.put(NCHAR, JAVA_STRING); - SQL_ENUM_JAVA.put(DATE, JAVA_UTILDATE); - SQL_ENUM_JAVA.put(DECIMAL, JAVA_BIGDECIMAL); - SQL_ENUM_JAVA.put(DOUBLE, JAVA_DOUBLE); - SQL_ENUM_JAVA.put(FLOAT, JAVA_FLOAT); - SQL_ENUM_JAVA.put(INTEGER, JAVA_INTEGER); - SQL_ENUM_JAVA.put(LONGVARCHAR, JAVA_STRING); - SQL_ENUM_JAVA.put(LONGNVARCHAR, JAVA_STRING); - SQL_ENUM_JAVA.put(LONGVARBINARY, JAVA_BYTES); - SQL_ENUM_JAVA.put(NUMERIC, JAVA_BIGDECIMAL); - SQL_ENUM_JAVA.put(REAL, JAVA_FLOAT); - SQL_ENUM_JAVA.put(SMALLINT, JAVA_SHORT); - SQL_ENUM_JAVA.put(TINYINT, JAVA_SHORT); - SQL_ENUM_JAVA.put(TIME, JAVA_UTILDATE); - SQL_ENUM_JAVA.put(TIMESTAMP, JAVA_UTILDATE); - SQL_ENUM_JAVA.put(VARBINARY, JAVA_BYTES); - SQL_ENUM_JAVA.put(VARCHAR, JAVA_STRING); - SQL_ENUM_JAVA.put(NVARCHAR, JAVA_STRING); - SQL_ENUM_JAVA.put(SQLXML, JAVA_STRING); - - JAVA_SQL_ENUM.put(JAVA_LONG, BIGINT); - JAVA_SQL_ENUM.put(JAVA_BYTES, BINARY); - JAVA_SQL_ENUM.put(JAVA_BOOLEAN, BIT); - JAVA_SQL_ENUM.put(JAVA_STRING, VARCHAR); - JAVA_SQL_ENUM.put(JAVA_SQLDATE, DATE); - JAVA_SQL_ENUM.put(JAVA_UTILDATE, DATE); - JAVA_SQL_ENUM.put(JAVA_TIMESTAMP, TIMESTAMP); - JAVA_SQL_ENUM.put(JAVA_BIGDECIMAL, DECIMAL); - JAVA_SQL_ENUM.put(JAVA_DOUBLE, DOUBLE); - JAVA_SQL_ENUM.put(JAVA_FLOAT, FLOAT); - JAVA_SQL_ENUM.put(JAVA_INTEGER, INTEGER); - JAVA_SQL_ENUM.put(JAVA_SHORT, SMALLINT); - JAVA_SQL_ENUM.put(JAVA_BYTE, SMALLINT); - JAVA_SQL_ENUM.put(JAVA_TIME, TIME); - - // add primitives - JAVA_SQL_ENUM.put("byte", TINYINT); - JAVA_SQL_ENUM.put("int", INTEGER); - JAVA_SQL_ENUM.put("short", SMALLINT); - JAVA_SQL_ENUM.put("char", CHAR); - JAVA_SQL_ENUM.put("double", DOUBLE); - JAVA_SQL_ENUM.put("long", BIGINT); - JAVA_SQL_ENUM.put("float", FLOAT); - JAVA_SQL_ENUM.put("boolean", BIT); - } - - // TODO: redo all isXyz as an internal enum over types, where each enum object knows whether it is this or that kind - - /** - * Returns true if supplied type is a character type. - * @since 4.0 - * @param type JDBC type - * @return true if supplied type is a character type. - */ - public static boolean isCharacter(int type) { - return type == Types.CHAR || type == Types.NCHAR || type == Types.VARCHAR || type == Types.NVARCHAR - || type == Types.CLOB || type == Types.NCLOB || type == Types.LONGVARCHAR || type == Types.LONGNVARCHAR; - } - - /** - * Returns true if supplied type is a binary type. - * @since 4.0 - * @param type JDBC type - * @return true if supplied type is a binary type. - */ - public static boolean isBinary(int type) { - return type == Types.BINARY || type == Types.BLOB || type == Types.VARBINARY || type == Types.LONGVARBINARY; - } - - /** - * Returns true if supplied type is a numeric type. - */ - public static boolean isNumeric(int type) { - return type == BIGINT || type == BIT || type == DECIMAL || type == DOUBLE || type == FLOAT || type == INTEGER - || type == NUMERIC || type == REAL || type == SMALLINT || type == TINYINT; - } - - /** - * Returns true if supplied type is a decimal type. - */ - public static boolean isDecimal(int type) { - return type == DECIMAL || type == DOUBLE || type == FLOAT || type == REAL || type == NUMERIC; - } - - /** - * @since 5.0 - */ - public static boolean isDateTime(int type) { - return type == TIME || type == TIMESTAMP; - } - - /** - * Returns an array of string names of the default JDBC data types. - */ - public static String[] getDatabaseTypes() { - Collection<String> types = SQL_STRING_TYPE.keySet(); - return types.toArray(new String[0]); - } - - /** - * Method implements an algorithm to pick a data type from a list of - * alternatives that most closely matches JDBC data type. - */ - protected static String pickDataType(int jdbcType, TypeInfo[] alts) { - int len = alts.length; - - if (len == 0) { - return null; - } - - if (len == 1) { - return alts[0].name; - } - - // now the fun starts.. try to guess the right type - - String jdbcName = getSqlNameByType(jdbcType).toUpperCase(); - - // 1. exact match - for (TypeInfo alt : alts) { - if (jdbcName.equalsIgnoreCase(alt.name)) { - return alt.name; - } - } - - // 2. filter those with biggest precision - long maxPrec = 0; - for (TypeInfo alt : alts) { - if (maxPrec < alt.precision) { - maxPrec = alt.precision; - } - } - - List<TypeInfo> list = new ArrayList<>(); - for (TypeInfo alt : alts) { - if (maxPrec == alt.precision) { - list.add(alt); - } - } - - // work with smaller list now..... - int slen = list.size(); - if (slen == 1) { - return list.get(0).name; - } - - // start/end match - for (TypeInfo aList : list) { - String uppercase = aList.name.toUpperCase(); - if (uppercase.startsWith(jdbcName) || uppercase.endsWith(jdbcName)) { - return aList.name; - } - } - - // in the middle match - for (TypeInfo aList : list) { - String uppercase = aList.name.toUpperCase(); - - if (uppercase.contains(jdbcName)) { - return aList.name; - } - } - - // out of ideas... return the first one - return list.get(0).name; - } - - /** - * Returns a JDBC int type for SQL typem name. - */ - public static int getSqlTypeByName(String typeName) { - Integer tmp = SQL_STRING_TYPE.get(typeName); - return tmp == null ? NOT_DEFINED : tmp; - } - - /** - * Returns a String representation of the SQL type from its JDBC code. - */ - public static String getSqlNameByType(int type) { - return SQL_ENUM_TYPE.get(type); - } - - /** - * Returns default java.sql.Types type by the Java type name. - * - * @param className - * Fully qualified Java Class name. - * @return The SQL type or NOT_DEFINED if no type found. - */ - public static int getSqlTypeByJava(String className) { - if (className == null) { - return NOT_DEFINED; - } - - Integer type = JAVA_SQL_ENUM.get(className); - if (type != null) { - return type; - } - - // try to load a Java class - some nonstandard mappings may work - - Class<?> aClass; - try { - aClass = Util.getJavaClass(className); - } catch (Throwable th) { - return NOT_DEFINED; - } - - return getSqlTypeByJava(aClass); - } - - /** - * Guesses a default JDBC type for the Java class. - * - * @since 1.1 - */ - public static int getSqlTypeByJava(Class<?> javaClass) { - if (javaClass == null) { - return NOT_DEFINED; - } - - // check standard mapping of class and superclasses - Class<?> aClass = javaClass; - while (aClass != null) { - - String name; - - if (aClass.isArray()) { - name = aClass.getComponentType().getName() + "[]"; - } else { - name = aClass.getName(); - } - - Number type = JAVA_SQL_ENUM.get(name); - if (type != null) { - return type.intValue(); - } - - aClass = aClass.getSuperclass(); - } - - // check non-standard JDBC types that are still supported by JPA - if (javaClass.isArray()) { - - Class<?> elementType = javaClass.getComponentType(); - if (Character.class.isAssignableFrom(elementType) || Character.TYPE.isAssignableFrom(elementType)) { - return VARCHAR; - } else if (Byte.class.isAssignableFrom(elementType) || Byte.TYPE.isAssignableFrom(elementType)) { - return VARBINARY; - } - } - - if (Calendar.class.isAssignableFrom(javaClass)) { - return TIMESTAMP; - } - - if (BigInteger.class.isAssignableFrom(javaClass)) { - return BIGINT; - } - - // serializable check should be the last one when all other mapping - // attempts - // failed - if (Serializable.class.isAssignableFrom(javaClass)) { - return VARBINARY; - } - - return NOT_DEFINED; - } - - /** - * Get the corresponding Java type by its {@link java.sql.Types} counterpart. Note - * that this method should be used as a last resort, with explicit mapping - * provided by user used as a first choice, as it can only guess how to map - * certain types, such as NUMERIC, etc. - * - * @param type as defined in {@link java.sql.Types} - * @return Fully qualified Java type name or null if not found. - */ - public static String getJavaBySqlType(int type) { - return SQL_ENUM_JAVA.get(type); - } - - /** - * @param attribute to get java type for - * @return Fully qualified Java type name or null if not found. - * @see #getJavaBySqlType(int) - * - * @since 4.2 - */ - public static String getJavaBySqlType(DbAttribute attribute) { - if(attribute.getType() == DECIMAL) { - if(attribute.getScale() == 0) { - // integer value, could fold into a smaller type - if (attribute.getMaxLength() < 10) { - return JAVA_INTEGER; - } else if(attribute.getMaxLength() < 19) { - return JAVA_LONG; - } else { - return JAVA_BIGINTEGER; - } - } else { - // decimal, no optimizations here - return JAVA_BIGDECIMAL; - } - } - - return SQL_ENUM_JAVA.get(attribute.getType()); - } - - Map<Integer, List<TypeInfo>> databaseTypes = new HashMap<>(); - - public TypesMapping(DatabaseMetaData metaData) throws SQLException { - // map database types to standard JDBC types - - try (ResultSet rs = metaData.getTypeInfo()) { - while (rs.next()) { - TypeInfo info = new TypeInfo(); - info.name = rs.getString("TYPE_NAME"); - info.jdbcType = rs.getInt("DATA_TYPE"); - info.precision = rs.getLong("PRECISION"); - - Integer key = info.jdbcType; - List<TypeInfo> infos = databaseTypes.computeIfAbsent(key, k -> new ArrayList<>()); - - infos.add(info); - } - } - - // do some tricks to substitute for missing datatypes - - // 1. swap TIMESTAMP - DATE - swapTypes(TIMESTAMP, DATE); - - // 2. Swap CLOB - LONGVARCHAR - swapTypes(CLOB, LONGVARCHAR); - - // 3. Swap BLOB - LONGVARBINARY - swapTypes(BLOB, LONGVARBINARY); - - // 4. Swap NCLOB - LONGNVARCHAR - swapTypes(NCLOB, LONGNVARCHAR); - } - - private void swapTypes(int type1, int type2) { - List<TypeInfo> type1Info = databaseTypes.get(type1); - List<TypeInfo> type2Info = databaseTypes.get(type2); - - if (type1Info != null && type2Info == null) { - databaseTypes.put(type2, type1Info); - } - if (type2Info != null && type1Info == null) { - databaseTypes.put(type1, type2Info); - } - } - - /** Stores (incomplete) information about database data type */ - static class TypeInfo { - - String name; - int jdbcType; - long precision; - - @Override - public String toString() { - return """ - [ TypeInfo: %s - JDBC Type: %s - Precision: %s - ]""".formatted(name, TypesMapping.getSqlNameByType(jdbcType), precision); - } - } + // Never use "-1" or any other normal integer, since there + // is a big chance it is being reserved in java.sql.Types + public static final int NOT_DEFINED = Integer.MAX_VALUE; + + // char constants for the sql data types + public static final String SQL_ARRAY = "ARRAY"; + public static final String SQL_BIGINT = "BIGINT"; + public static final String SQL_BINARY = "BINARY"; + public static final String SQL_BIT = "BIT"; + public static final String SQL_BLOB = "BLOB"; + + /** + * @since 1.2 + */ + public static final String SQL_BOOLEAN = "BOOLEAN"; + + public static final String SQL_CLOB = "CLOB"; + public static final String SQL_NCLOB = "NCLOB"; + public static final String SQL_CHAR = "CHAR"; + public static final String SQL_NCHAR = "NCHAR"; + public static final String SQL_DATE = "DATE"; + public static final String SQL_DECIMAL = "DECIMAL"; + public static final String SQL_DOUBLE = "DOUBLE"; + public static final String SQL_FLOAT = "FLOAT"; + public static final String SQL_INTEGER = "INTEGER"; + public static final String SQL_LONGVARCHAR = "LONGVARCHAR"; + public static final String SQL_LONGNVARCHAR = "LONGNVARCHAR"; + public static final String SQL_LONGVARBINARY = "LONGVARBINARY"; + public static final String SQL_NUMERIC = "NUMERIC"; + public static final String SQL_REAL = "REAL"; + public static final String SQL_SMALLINT = "SMALLINT"; + public static final String SQL_TINYINT = "TINYINT"; + public static final String SQL_TIME = "TIME"; + public static final String SQL_TIMESTAMP = "TIMESTAMP"; + public static final String SQL_VARBINARY = "VARBINARY"; + public static final String SQL_VARCHAR = "VARCHAR"; + public static final String SQL_NVARCHAR = "NVARCHAR"; + public static final String SQL_SQLXML = "SQLXML"; + public static final String SQL_OTHER = "OTHER"; + public static final String SQL_NULL = "NULL"; + + // char constants for Java data types + public static final String JAVA_LONG = "java.lang.Long"; + public static final String JAVA_BYTES = "byte[]"; + public static final String JAVA_BOOLEAN = "java.lang.Boolean"; + public static final String JAVA_STRING = "java.lang.String"; + public static final String JAVA_SQLDATE = "java.sql.Date"; + public static final String JAVA_UTILDATE = "java.util.Date"; + public static final String JAVA_BIGDECIMAL = "java.math.BigDecimal"; + public static final String JAVA_BIGINTEGER = "java.math.BigInteger"; + public static final String JAVA_DOUBLE = "java.lang.Double"; + public static final String JAVA_FLOAT = "java.lang.Float"; + public static final String JAVA_INTEGER = "java.lang.Integer"; + public static final String JAVA_SHORT = "java.lang.Short"; + public static final String JAVA_BYTE = "java.lang.Byte"; + public static final String JAVA_TIME = "java.sql.Time"; + public static final String JAVA_TIMESTAMP = "java.sql.Timestamp"; + public static final String JAVA_BLOB = "java.sql.Blob"; + + /** + * Keys: SQL string type names, Values: SQL int type definitions from + * java.sql.Types + */ + private static final Map<String, Integer> SQL_STRING_TYPE = new HashMap<>(); + + /** + * Keys: SQL int type definitions from java.sql.Types, Values: SQL string + * type names + */ + private static final Map<Integer, String> SQL_ENUM_TYPE = new HashMap<>(); + + /** + * Keys: SQL int type definitions from java.sql.Types, Values: java class + * names + */ + private static final Map<Integer, String> SQL_ENUM_JAVA = new HashMap<>(); + + /** + * Keys: java class names, Values: SQL int type definitions from + * java.sql.Types + */ + private static final Map<String, Integer> JAVA_SQL_ENUM = new HashMap<>(); + + static { + // SQL_STRING_TYPE.put(SQL_ARRAY, ARRAY); + SQL_STRING_TYPE.put(SQL_BIGINT, BIGINT); + SQL_STRING_TYPE.put(SQL_BINARY, BINARY); + SQL_STRING_TYPE.put(SQL_BIT, BIT); + SQL_STRING_TYPE.put(SQL_BLOB, BLOB); + SQL_STRING_TYPE.put(SQL_BOOLEAN, BOOLEAN); + SQL_STRING_TYPE.put(SQL_CLOB, CLOB); + SQL_STRING_TYPE.put(SQL_NCLOB, NCLOB); + SQL_STRING_TYPE.put(SQL_CHAR, CHAR); + SQL_STRING_TYPE.put(SQL_NCHAR, NCHAR); + SQL_STRING_TYPE.put(SQL_DATE, DATE); + SQL_STRING_TYPE.put(SQL_DECIMAL, DECIMAL); + SQL_STRING_TYPE.put(SQL_DOUBLE, DOUBLE); + SQL_STRING_TYPE.put(SQL_FLOAT, FLOAT); + SQL_STRING_TYPE.put(SQL_INTEGER, INTEGER); + SQL_STRING_TYPE.put(SQL_LONGVARCHAR, LONGVARCHAR); + SQL_STRING_TYPE.put(SQL_LONGNVARCHAR, LONGNVARCHAR); + SQL_STRING_TYPE.put(SQL_LONGVARBINARY, LONGVARBINARY); + SQL_STRING_TYPE.put(SQL_NUMERIC, NUMERIC); + SQL_STRING_TYPE.put(SQL_REAL, REAL); + SQL_STRING_TYPE.put(SQL_SMALLINT, SMALLINT); + SQL_STRING_TYPE.put(SQL_TINYINT, TINYINT); + SQL_STRING_TYPE.put(SQL_TIME, TIME); + SQL_STRING_TYPE.put(SQL_TIMESTAMP, TIMESTAMP); + SQL_STRING_TYPE.put(SQL_VARBINARY, VARBINARY); + SQL_STRING_TYPE.put(SQL_VARCHAR, VARCHAR); + SQL_STRING_TYPE.put(SQL_NVARCHAR, NVARCHAR); + SQL_STRING_TYPE.put(SQL_OTHER, OTHER); + SQL_STRING_TYPE.put(SQL_NULL, NULL); + + SQL_ENUM_TYPE.put(ARRAY, SQL_ARRAY); + SQL_ENUM_TYPE.put(BIGINT, SQL_BIGINT); + SQL_ENUM_TYPE.put(BINARY, SQL_BINARY); + SQL_ENUM_TYPE.put(BIT, SQL_BIT); + SQL_ENUM_TYPE.put(BOOLEAN, SQL_BOOLEAN); + SQL_ENUM_TYPE.put(BLOB, SQL_BLOB); + SQL_ENUM_TYPE.put(CLOB, SQL_CLOB); + SQL_ENUM_TYPE.put(NCLOB, SQL_NCLOB); + SQL_ENUM_TYPE.put(CHAR, SQL_CHAR); + SQL_ENUM_TYPE.put(NCHAR, SQL_NCHAR); + SQL_ENUM_TYPE.put(DATE, SQL_DATE); + SQL_ENUM_TYPE.put(DECIMAL, SQL_DECIMAL); + SQL_ENUM_TYPE.put(DOUBLE, SQL_DOUBLE); + SQL_ENUM_TYPE.put(FLOAT, SQL_FLOAT); + SQL_ENUM_TYPE.put(INTEGER, SQL_INTEGER); + SQL_ENUM_TYPE.put(LONGVARCHAR, SQL_LONGVARCHAR); + SQL_ENUM_TYPE.put(LONGNVARCHAR, SQL_LONGNVARCHAR); + SQL_ENUM_TYPE.put(LONGVARBINARY, SQL_LONGVARBINARY); + SQL_ENUM_TYPE.put(NUMERIC, SQL_NUMERIC); + SQL_ENUM_TYPE.put(REAL, SQL_REAL); + SQL_ENUM_TYPE.put(SMALLINT, SQL_SMALLINT); + SQL_ENUM_TYPE.put(TINYINT, SQL_TINYINT); + SQL_ENUM_TYPE.put(TIME, SQL_TIME); + SQL_ENUM_TYPE.put(TIMESTAMP, SQL_TIMESTAMP); + SQL_ENUM_TYPE.put(VARBINARY, SQL_VARBINARY); + SQL_ENUM_TYPE.put(VARCHAR, SQL_VARCHAR); + SQL_ENUM_TYPE.put(NVARCHAR, SQL_NVARCHAR); + SQL_ENUM_TYPE.put(SQLXML, SQL_SQLXML); + SQL_ENUM_TYPE.put(OTHER, SQL_OTHER); + SQL_ENUM_TYPE.put(NULL, SQL_NULL); + + SQL_ENUM_JAVA.put(BIGINT, JAVA_LONG); + SQL_ENUM_JAVA.put(BINARY, JAVA_BYTES); + SQL_ENUM_JAVA.put(BIT, JAVA_BOOLEAN); + SQL_ENUM_JAVA.put(BOOLEAN, JAVA_BOOLEAN); + SQL_ENUM_JAVA.put(BLOB, JAVA_BYTES); + SQL_ENUM_JAVA.put(CLOB, JAVA_STRING); + SQL_ENUM_JAVA.put(NCLOB, JAVA_STRING); + SQL_ENUM_JAVA.put(CHAR, JAVA_STRING); + SQL_ENUM_JAVA.put(NCHAR, JAVA_STRING); + SQL_ENUM_JAVA.put(DATE, JAVA_UTILDATE); + SQL_ENUM_JAVA.put(DECIMAL, JAVA_BIGDECIMAL); + SQL_ENUM_JAVA.put(DOUBLE, JAVA_DOUBLE); + SQL_ENUM_JAVA.put(FLOAT, JAVA_FLOAT); + SQL_ENUM_JAVA.put(INTEGER, JAVA_INTEGER); + SQL_ENUM_JAVA.put(LONGVARCHAR, JAVA_STRING); + SQL_ENUM_JAVA.put(LONGNVARCHAR, JAVA_STRING); + SQL_ENUM_JAVA.put(LONGVARBINARY, JAVA_BYTES); + SQL_ENUM_JAVA.put(NUMERIC, JAVA_BIGDECIMAL); + SQL_ENUM_JAVA.put(REAL, JAVA_FLOAT); + SQL_ENUM_JAVA.put(SMALLINT, JAVA_SHORT); + SQL_ENUM_JAVA.put(TINYINT, JAVA_SHORT); + SQL_ENUM_JAVA.put(TIME, JAVA_UTILDATE); + SQL_ENUM_JAVA.put(TIMESTAMP, JAVA_UTILDATE); + SQL_ENUM_JAVA.put(VARBINARY, JAVA_BYTES); + SQL_ENUM_JAVA.put(VARCHAR, JAVA_STRING); + SQL_ENUM_JAVA.put(NVARCHAR, JAVA_STRING); + SQL_ENUM_JAVA.put(SQLXML, JAVA_STRING); + + JAVA_SQL_ENUM.put(JAVA_LONG, BIGINT); + JAVA_SQL_ENUM.put(JAVA_BYTES, BINARY); + JAVA_SQL_ENUM.put(JAVA_BOOLEAN, BIT); + JAVA_SQL_ENUM.put(JAVA_STRING, VARCHAR); + JAVA_SQL_ENUM.put(JAVA_SQLDATE, DATE); + JAVA_SQL_ENUM.put(JAVA_UTILDATE, DATE); + JAVA_SQL_ENUM.put(JAVA_TIMESTAMP, TIMESTAMP); + JAVA_SQL_ENUM.put(JAVA_BIGDECIMAL, DECIMAL); + JAVA_SQL_ENUM.put(JAVA_DOUBLE, DOUBLE); + JAVA_SQL_ENUM.put(JAVA_FLOAT, FLOAT); + JAVA_SQL_ENUM.put(JAVA_INTEGER, INTEGER); + JAVA_SQL_ENUM.put(JAVA_SHORT, SMALLINT); + JAVA_SQL_ENUM.put(JAVA_BYTE, SMALLINT); + JAVA_SQL_ENUM.put(JAVA_TIME, TIME); + + // add primitives + JAVA_SQL_ENUM.put("byte", TINYINT); + JAVA_SQL_ENUM.put("int", INTEGER); + JAVA_SQL_ENUM.put("short", SMALLINT); + JAVA_SQL_ENUM.put("char", CHAR); + JAVA_SQL_ENUM.put("double", DOUBLE); + JAVA_SQL_ENUM.put("long", BIGINT); + JAVA_SQL_ENUM.put("float", FLOAT); + JAVA_SQL_ENUM.put("boolean", BIT); + } + + // TODO: redo all isXyz as an internal enum over types, where each enum object knows whether it is this or that kind + + /** + * Returns true if supplied type is a character type. + * + * @param type JDBC type + * @return true if supplied type is a character type. + * @since 4.0 + */ + public static boolean isCharacter(int type) { + return type == Types.CHAR || type == Types.NCHAR || type == Types.VARCHAR || type == Types.NVARCHAR + || type == Types.CLOB || type == Types.NCLOB || type == Types.LONGVARCHAR || type == Types.LONGNVARCHAR; + } + + /** + * Returns true if supplied type is a binary type. + * + * @param type JDBC type + * @return true if supplied type is a binary type. + * @since 4.0 + */ + public static boolean isBinary(int type) { + return type == Types.BINARY || type == Types.BLOB || type == Types.VARBINARY || type == Types.LONGVARBINARY; + } + + /** + * Returns true if supplied type is a numeric type. + */ + public static boolean isNumeric(int type) { + return type == BIGINT || type == BIT || type == DECIMAL || type == DOUBLE || type == FLOAT || type == INTEGER + || type == NUMERIC || type == REAL || type == SMALLINT || type == TINYINT; + } + + /** + * Returns true if supplied type is a decimal type. + */ + public static boolean isDecimal(int type) { + return type == DECIMAL || type == DOUBLE || type == FLOAT || type == REAL || type == NUMERIC; + } + + /** + * @since 5.0 + */ + public static boolean isDateTime(int type) { + return type == TIME || type == TIMESTAMP; + } + + /** + * Returns an array of string names of the default JDBC data types. + */ + public static String[] getDatabaseTypes() { + Collection<String> types = SQL_STRING_TYPE.keySet(); + return types.toArray(new String[0]); + } + + /** + * Method implements an algorithm to pick a data type from a list of + * alternatives that most closely matches JDBC data type. + */ + protected static String pickDataType(int jdbcType, TypeInfo[] alts) { + int len = alts.length; + + if (len == 0) { + return null; + } + + if (len == 1) { + return alts[0].name; + } + + // now the fun starts.. try to guess the right type + + String jdbcName = getSqlNameByType(jdbcType).toUpperCase(); + + // 1. exact match + for (TypeInfo alt : alts) { + if (jdbcName.equalsIgnoreCase(alt.name)) { + return alt.name; + } + } + + // 2. filter those with biggest precision + long maxPrec = 0; + for (TypeInfo alt : alts) { + if (maxPrec < alt.precision) { + maxPrec = alt.precision; + } + } + + List<TypeInfo> list = new ArrayList<>(); + for (TypeInfo alt : alts) { + if (maxPrec == alt.precision) { + list.add(alt); + } + } + + // work with smaller list now..... + int slen = list.size(); + if (slen == 1) { + return list.get(0).name; + } + + // start/end match + for (TypeInfo aList : list) { + String uppercase = aList.name.toUpperCase(); + if (uppercase.startsWith(jdbcName) || uppercase.endsWith(jdbcName)) { + return aList.name; + } + } + + // in the middle match + for (TypeInfo aList : list) { + String uppercase = aList.name.toUpperCase(); + + if (uppercase.contains(jdbcName)) { + return aList.name; + } + } + + // out of ideas... return the first one + return list.get(0).name; + } + + /** + * Returns a JDBC int type for SQL typem name. + */ + public static int getSqlTypeByName(String typeName) { + Integer tmp = SQL_STRING_TYPE.get(typeName); + return tmp == null ? NOT_DEFINED : tmp; + } + + /** + * Returns a String representation of the SQL type from its JDBC code. + */ + public static String getSqlNameByType(int type) { + return SQL_ENUM_TYPE.get(type); + } + + /** + * Returns default java.sql.Types type by the Java type name. + * + * @param className Fully qualified Java Class name. + * @return The SQL type or NOT_DEFINED if no type found. + */ + public static int getSqlTypeByJava(String className) { + if (className == null) { + return NOT_DEFINED; + } + + Integer type = JAVA_SQL_ENUM.get(className); + if (type != null) { + return type; + } + + // try to load a Java class - some nonstandard mappings may work + + Class<?> aClass; + try { + aClass = Util.getJavaClass(className); + } catch (Throwable th) { + return NOT_DEFINED; + } + + return getSqlTypeByJava(aClass); + } + + /** + * Guesses a default JDBC type for the Java class. + * + * @since 1.1 + */ + public static int getSqlTypeByJava(Class<?> javaClass) { + if (javaClass == null) { + return NOT_DEFINED; + } + + // check standard mapping of class and superclasses + Class<?> aClass = javaClass; + while (aClass != null) { + + String name; + + if (aClass.isArray()) { + name = aClass.getComponentType().getName() + "[]"; + } else { + name = aClass.getName(); + } + + Number type = JAVA_SQL_ENUM.get(name); + if (type != null) { + return type.intValue(); + } + + aClass = aClass.getSuperclass(); + } + + // check non-standard JDBC types that are still supported by JPA + if (javaClass.isArray()) { + + Class<?> elementType = javaClass.getComponentType(); + if (Character.class.isAssignableFrom(elementType) || Character.TYPE.isAssignableFrom(elementType)) { + return VARCHAR; + } else if (Byte.class.isAssignableFrom(elementType) || Byte.TYPE.isAssignableFrom(elementType)) { + return VARBINARY; + } + } + + if (Calendar.class.isAssignableFrom(javaClass)) { + return TIMESTAMP; + } + + if (BigInteger.class.isAssignableFrom(javaClass)) { + return BIGINT; + } + + // serializable check should be the last one when all other mapping + // attempts + // failed + if (Serializable.class.isAssignableFrom(javaClass)) { + return VARBINARY; + } + + return NOT_DEFINED; + } + + /** + * Get the corresponding Java type by its {@link java.sql.Types} counterpart. Note + * that this method should be used as a last resort, with explicit mapping + * provided by user used as a first choice, as it can only guess how to map + * certain types, such as NUMERIC, etc. + * + * @param type as defined in {@link java.sql.Types} + * @return Fully qualified Java type name or null if not found. + */ + public static String getJavaBySqlType(int type) { + return SQL_ENUM_JAVA.get(type); + } + + /** + * @param attribute to get java type for + * @return Fully qualified Java type name or null if not found. + * @see #getJavaBySqlType(int) + * @since 4.2 + */ + public static String getJavaBySqlType(DbAttribute attribute) { + if (attribute.getType() == DECIMAL) { + if (attribute.getScale() == 0) { + // integer value, could fold into a smaller type + if (attribute.getMaxLength() < 10) { + return JAVA_INTEGER; + } else if (attribute.getMaxLength() < 19) { + return JAVA_LONG; + } else { + return JAVA_BIGINTEGER; + } + } else { + // decimal, no optimizations here + return JAVA_BIGDECIMAL; + } + } + + return SQL_ENUM_JAVA.get(attribute.getType()); + } + + Map<Integer, List<TypeInfo>> databaseTypes = new HashMap<>(); + + public TypesMapping(DatabaseMetaData metaData) throws SQLException { + // map database types to standard JDBC types + + try (ResultSet rs = metaData.getTypeInfo()) { + while (rs.next()) { + TypeInfo info = new TypeInfo(); + info.name = rs.getString("TYPE_NAME"); + info.jdbcType = rs.getInt("DATA_TYPE"); + info.precision = rs.getLong("PRECISION"); + + Integer key = info.jdbcType; + List<TypeInfo> infos = databaseTypes.computeIfAbsent(key, k -> new ArrayList<>()); + + infos.add(info); + } + } + + // do some tricks to substitute for missing datatypes + + // 1. swap TIMESTAMP - DATE + swapTypes(TIMESTAMP, DATE); + + // 2. Swap CLOB - LONGVARCHAR + swapTypes(CLOB, LONGVARCHAR); + + // 3. Swap BLOB - LONGVARBINARY + swapTypes(BLOB, LONGVARBINARY); + + // 4. Swap NCLOB - LONGNVARCHAR + swapTypes(NCLOB, LONGNVARCHAR); + } + + private void swapTypes(int type1, int type2) { + List<TypeInfo> type1Info = databaseTypes.get(type1); + List<TypeInfo> type2Info = databaseTypes.get(type2); + + if (type1Info != null && type2Info == null) { + databaseTypes.put(type2, type1Info); + } + if (type2Info != null && type1Info == null) { + databaseTypes.put(type1, type2Info); + } + } + + /** + * Stores (incomplete) information about database data type + */ + static class TypeInfo { + + String name; + int jdbcType; + long precision; + + @Override + public String toString() { + return """ + [ TypeInfo: %s + JDBC Type: %s + Precision: %s + ]""".formatted(name, TypesMapping.getSqlNameByType(jdbcType), precision); + } + } } diff --git a/cayenne/src/main/java/org/apache/cayenne/map/DbAttribute.java b/cayenne/src/main/java/org/apache/cayenne/map/DbAttribute.java index 28a2b92cb..1b9f2b4c5 100644 --- a/cayenne/src/main/java/org/apache/cayenne/map/DbAttribute.java +++ b/cayenne/src/main/java/org/apache/cayenne/map/DbAttribute.java @@ -282,7 +282,7 @@ public class DbAttribute extends Attribute<DbEntity, DbAttribute, DbRelationship * @return java class guessed by the JDBC type of this attribute * @since 5.0 */ - public String getJavaClass() { + public String getJavaClassName() { return TypesMapping.getJavaBySqlType(this); }
