I defined an entity class:

class Clazz{
    private long id;
    private String cityHash;
    public Clazz(){}
    public Clazz(String cityHash){
        this.cityHash = cityHash
    }
}

mysql: table city_hash

    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `city_hash` bigint(20) unsigned NOT NULL COMMENT'Google city hash64',

I use SimpleJdbcInsert to insert data:

    private SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(getJdbcTemplate()).withTableName("city_hash").usingGeneratedKeyColumns("id");
    Clazz clazz = new Clazz("12040412458563268888")
    Number newId = simpleJdbcInsert.executeAndReturnKey(clazz);

Exception:

Caused by: org.springframework.dao.TransientDataAccessResourceException: PreparedStatementCallback; SQL []; 
Cannot convert class java.lang.String to SQL type requested due to java.lang.NumberFormatException- For input string: "12040412458563268888"; 
nested exception is java. sql.SQLException: Cannot convert class java.lang.String to SQL type requested due to java.lang.NumberFormatException-For input string: "12040412458563268888"

I encountered this exception in spring 3+, it can insert data in spring 2.5. I think for database bigint(20) unsigned should not be automatically mapped as Long in java

Source code: (fun: AbstractJdbcInsert.executeInsertAndReturnKeyHolderInternal(List)) 3+

        if (this.tableMetaDataContext.isGetGeneratedKeysSupported()) {
            getJdbcTemplate().update(
                    new PreparedStatementCreator() {
                        public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                            PreparedStatement ps = prepareStatementForGeneratedKeys(con);
                            setParameterValues(ps, values, getInsertTypes());
                            return ps;
                        }
                    },
                    keyHolder);
        }

2.5

        if (this.tableMetaDataContext.isGetGeneratedKeysSupported()) {
            this.jdbcTemplate.update(new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
                    PreparedStatement ps = AbstractJdbcInsert.this.prepareStatementForGeneratedKeys(con);
                    AbstractJdbcInsert.this.setParameterValues(ps, values, (int[])null);
                    return ps;
                }
            }, keyHolder);
            return keyHolder;
        }

Comment From: rstoyanchev

Version 3+ is unclear. If you mean 3.x, that branch is old and unsupported. For a list of currently supported versions, see the support page. If you could, please confirm the actual version you have tried this with?

Comment From: EwainCai

@rstoyanchev Yes, I tested it on spring 3.2.8, spring 4.2.8, spring 5.2.19 All versions after spring3+ have this issue.

Comment From: EwainCai

spring 5.2.19

        if (this.tableMetaDataContext.isGetGeneratedKeysSupported()) {
            this.getJdbcTemplate().update((con) -> {
                PreparedStatement ps = this.prepareStatementForGeneratedKeys(con);
                this.setParameterValues(ps, values, this.getInsertTypes());
                return ps;
            }, keyHolder);
        }

Comment From: EwainCai

org.springframework.jdbc.core.StatementCreatorUtils:

    private static void setValue(PreparedStatement ps, int paramIndex, int sqlType,
            @Nullable String typeName, @Nullable Integer scale, Object inValue) throws SQLException {

        if (inValue instanceof SqlTypeValue) {
            ((SqlTypeValue) inValue).setTypeValue(ps, paramIndex, sqlType, typeName);
        }
        else if (inValue instanceof SqlValue) {
            ((SqlValue) inValue).setValue(ps, paramIndex);
        }
        else if (sqlType == Types.VARCHAR || sqlType == Types.LONGVARCHAR ) {
            ps.setString(paramIndex, inValue.toString());
        }
        else if (sqlType == Types.NVARCHAR || sqlType == Types.LONGNVARCHAR) {
            ps.setNString(paramIndex, inValue.toString());
        }
        else if ((sqlType == Types.CLOB || sqlType == Types.NCLOB) && isStringValue(inValue.getClass())) {
            String strVal = inValue.toString();
            if (strVal.length() > 4000) {
                // Necessary for older Oracle drivers, in particular when running against an Oracle 10 database.
                // Should also work fine against other drivers/databases since it uses standard JDBC 4.0 API.
                if (sqlType == Types.NCLOB) {
                    ps.setNClob(paramIndex, new StringReader(strVal), strVal.length());
                }
                else {
                    ps.setClob(paramIndex, new StringReader(strVal), strVal.length());
                }
            }
            else {
                // Fallback: setString or setNString binding
                if (sqlType == Types.NCLOB) {
                    ps.setNString(paramIndex, strVal);
                }
                else {
                    ps.setString(paramIndex, strVal);
                }
            }
        }
        else if (sqlType == Types.DECIMAL || sqlType == Types.NUMERIC) {
            if (inValue instanceof BigDecimal) {
                ps.setBigDecimal(paramIndex, (BigDecimal) inValue);
            }
            else if (scale != null) {
                ps.setObject(paramIndex, inValue, sqlType, scale);
            }
            else {
                ps.setObject(paramIndex, inValue, sqlType);
            }
        }
        else if (sqlType == Types.BOOLEAN) {
            if (inValue instanceof Boolean) {
                ps.setBoolean(paramIndex, (Boolean) inValue);
            }
            else {
                ps.setObject(paramIndex, inValue, Types.BOOLEAN);
            }
        }
        else if (sqlType == Types.DATE) {
            if (inValue instanceof java.util.Date) {
                if (inValue instanceof java.sql.Date) {
                    ps.setDate(paramIndex, (java.sql.Date) inValue);
                }
                else {
                    ps.setDate(paramIndex, new java.sql.Date(((java.util.Date) inValue).getTime()));
                }
            }
            else if (inValue instanceof Calendar) {
                Calendar cal = (Calendar) inValue;
                ps.setDate(paramIndex, new java.sql.Date(cal.getTime().getTime()), cal);
            }
            else {
                ps.setObject(paramIndex, inValue, Types.DATE);
            }
        }
        else if (sqlType == Types.TIME) {
            if (inValue instanceof java.util.Date) {
                if (inValue instanceof java.sql.Time) {
                    ps.setTime(paramIndex, (java.sql.Time) inValue);
                }
                else {
                    ps.setTime(paramIndex, new java.sql.Time(((java.util.Date) inValue).getTime()));
                }
            }
            else if (inValue instanceof Calendar) {
                Calendar cal = (Calendar) inValue;
                ps.setTime(paramIndex, new java.sql.Time(cal.getTime().getTime()), cal);
            }
            else {
                ps.setObject(paramIndex, inValue, Types.TIME);
            }
        }
        else if (sqlType == Types.TIMESTAMP) {
            if (inValue instanceof java.util.Date) {
                if (inValue instanceof java.sql.Timestamp) {
                    ps.setTimestamp(paramIndex, (java.sql.Timestamp) inValue);
                }
                else {
                    ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime()));
                }
            }
            else if (inValue instanceof Calendar) {
                Calendar cal = (Calendar) inValue;
                ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal);
            }
            else {
                ps.setObject(paramIndex, inValue, Types.TIMESTAMP);
            }
        }
        else if (sqlType == SqlTypeValue.TYPE_UNKNOWN || (sqlType == Types.OTHER &&
                "Oracle".equals(ps.getConnection().getMetaData().getDatabaseProductName()))) {
            if (isStringValue(inValue.getClass())) {
                ps.setString(paramIndex, inValue.toString());
            }
            else if (isDateValue(inValue.getClass())) {
                ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime()));
            }
            else if (inValue instanceof Calendar) {
                Calendar cal = (Calendar) inValue;
                ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal);
            }
            else {
                // Fall back to generic setObject call without SQL type specified.
                ps.setObject(paramIndex, inValue);
            }
        }
        else {
            // Fall back to generic setObject call with SQL type specified.
            ps.setObject(paramIndex, inValue, sqlType);
        }
    }

org.springframework.jdbc.core.metadata.GenericTableMetaDataProvider

    private void processTableColumns(DatabaseMetaData databaseMetaData, TableMetaData tmd) {
        ResultSet tableColumns = null;
        String metaDataCatalogName = metaDataCatalogNameToUse(tmd.getCatalogName());
        String metaDataSchemaName = metaDataSchemaNameToUse(tmd.getSchemaName());
        String metaDataTableName = tableNameToUse(tmd.getTableName());
        if (logger.isDebugEnabled()) {
            logger.debug("Retrieving meta-data for " + metaDataCatalogName + '/' +
                    metaDataSchemaName + '/' + metaDataTableName);
        }
        try {
            tableColumns = databaseMetaData.getColumns(
                    metaDataCatalogName, metaDataSchemaName, metaDataTableName, null);
            while (tableColumns.next()) {
                String columnName = tableColumns.getString("COLUMN_NAME");
                int dataType = tableColumns.getInt("DATA_TYPE");
                if (dataType == Types.DECIMAL) {
                    String typeName = tableColumns.getString("TYPE_NAME");
                    int decimalDigits = tableColumns.getInt("DECIMAL_DIGITS");
                    // Override a DECIMAL data type for no-decimal numerics
                    // (this is for better Oracle support where there have been issues
                    // using DECIMAL for certain inserts (see SPR-6912))
                    if ("NUMBER".equals(typeName) && decimalDigits == 0) {
                        dataType = Types.NUMERIC;
                        if (logger.isDebugEnabled()) {
                            logger.debug("Overriding meta-data: " + columnName + " now NUMERIC instead of DECIMAL");
                        }
                    }
                }
                boolean nullable = tableColumns.getBoolean("NULLABLE");
                TableParameterMetaData meta = new TableParameterMetaData(columnName, dataType, nullable);
                this.tableParameterMetaData.add(meta);
                if (logger.isDebugEnabled()) {
                    logger.debug("Retrieved meta-data: '" + meta.getParameterName() + "', sqlType=" +
                            meta.getSqlType() + ", nullable=" + meta.isNullable());
                }
            }
        }
        catch (SQLException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Error while retrieving meta-data for table columns. " +
                        "Consider specifying explicit column names -- for example, via SimpleJdbcInsert#usingColumns().",
                        ex);
            }
            // Clear the metadata so that we don't retain a partial list of column names
            this.tableParameterMetaData.clear();
        }
        finally {
            JdbcUtils.closeResultSet(tableColumns);
        }
    }

Maybe we not only need to get the 'DATA_TYPE', but also adapt the java types(java.sql.Types) according to the column length!

Comment From: EwainCai

Can someone please reply to this issue?

Comment From: sbrannen

Hi @EwainCai,

What database (and version) are you testing against?

In addition, SimpleJdbcInsert does not have an executeAndReturnKey(...) method that accepts an Object. So it appears that your Number newId = simpleJdbcInsert.executeAndReturnKey(clazz); example would not compile.

If you can provide us a minimal sample application that demonstrates the behavior you are experiencing (preferably via a GitHub repository or zip file that we can download and experiment with), that would significantly increase the likelihood that the team can investigate further.

Thanks

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

Comment From: EwainCai

Hi @EwainCai,

What database (and version) are you testing against?

In addition, SimpleJdbcInsert does not have an executeAndReturnKey(...) method that accepts an Object. So it appears that your Number newId = simpleJdbcInsert.executeAndReturnKey(clazz); example would not compile.

If you can provide us a minimal sample application that demonstrates the behavior you are experiencing (preferably via a GitHub repository or zip file that we can download and experiment with), that would significantly increase the likelihood that the team can investigate further.

Thanks

@sbrannen Sorry for providing wrong information.

The example below is the method I actually use.

spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java

    @Override
    public Number executeAndReturnKey(Map<String, ?> args) {
        return doExecuteAndReturnKey(args);
    }
    private SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(getJdbcTemplate())
            .withTableName("city_hash")
            .usingGeneratedKeyColumns("id");

    Map<String, String> map = new HashMap<>();
    map.put("city_hash", "12040412458563268888");
    Number newId = simpleJdbcInsert.executeAndReturnKey(map);

Comment From: snicoll

If you can provide us a minimal sample application that demonstrates the behavior you are experiencing (preferably via a GitHub repository or zip file that we can download and experiment with), that would significantly increase the likelihood that the team can investigate further.

Can you please provide that as I am still not sure what database you're using and we can't justify trying to guess what it is.

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.