【问题标题】:How to update a postgresql array column with spring JdbcTemplate?如何使用 spring JdbcTemplate 更新 postgresql 数组列?
【发布时间】:2019-03-28 20:53:40
【问题描述】:

我正在使用 Spring JdbcTemplate,但我遇到了一个查询,该查询更新了一个实际上是 int 数组的列。数据库是 postgres 8.3.7。 这是我正在使用的代码:

public int setUsersArray(int idUser, int idDevice, Collection<Integer> ids) {

    int update = -666;

    int[] tipi = new int[3];
    tipi[0] = java.sql.Types.INTEGER;
    tipi[1] = java.sql.Types.INTEGER;
    tipi[2] = java.sql.Types.ARRAY;

    try {
        update = this.jdbcTemplate.update(setUsersArrayQuery, new Object[] {
                ids, idUser, idDevice }, tipi);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return update;
}

查询是“update table_name set array_column = ? where id_user = ? and id_device = ?”。 我得到了这个例外:

org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [更新 acotel_msp.users_mau 设置 denied_sub_client = ?哪里 id_users = ?和 id_mau = ?];列索引超出范围:4,列数:3。嵌套异常是 org.postgresql.util.PSQLException:列索引超出范围:4,列数:3。

原因:org.postgresql.util.PSQLException:列索引超出范围:4,列数:3。

我已经查看了 spring jdbc 模板文档,但找不到任何帮助,我会继续寻找,无论如何有人能指出我正确的方向吗?谢谢!

编辑:

显然顺序错了,是我的错……

我尝试了你的两种解决方案,在第一种情况下我有这个:

org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback;错误的 SQL 语法 [更新用户集 denied_sub_client = ?哪里 id_users = ?和 id_device = ?];嵌套异常是 org.postgresql.util.PSQLException: Cannot cast an instance of java.util.ArrayList to type Types.ARRAY

尝试第二种解决方案我有这个:

org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback;错误的 SQL 语法 [更新用户集 denied_sub_client = ?哪里 id_users = ?和 id_device = ?];嵌套异常是 org.postgresql.util.PSQLException: Cannot cast an instance of [Ljava.lang.Object;键入 Types.ARRAY

我想我需要一个 java.sql.Array 的实例,但是如何使用 JdbcTemplate 创建它?

【问题讨论】:

    标签: arrays spring postgresql jdbctemplate


    【解决方案1】:

    经过多次尝试,我们决定使用一个小助手 ArraySqlValue 来为 Java Array Types 创建 Spring SqlValue 对象。

    用法是这样的

    jdbcTemplate.update(
                    "UPDATE sometable SET arraycolumn = ?",
                    ArraySqlValue.create(arrayValue))
    

    ArraySqlValue 也可以在 MapSqlParameterSource 中与 NamedParameterJdbcTemplate 一起使用。

    import static com.google.common.base.Preconditions.checkNotNull;
    
    import java.sql.Array;
    import java.sql.JDBCType;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.util.Locale;
    
    import org.springframework.jdbc.core.StatementCreatorUtils;
    import org.springframework.jdbc.support.SqlValue;
    
    public class ArraySqlValue implements SqlValue {
        private final Object[] arr;
        private final String   dbTypeName;
    
        public static ArraySqlValue create(final Object[] arr) {
            return new ArraySqlValue(arr, determineDbTypeName(arr));
        }
    
        public static ArraySqlValue create(final Object[] arr, final String dbTypeName) {
            return new ArraySqlValue(arr, dbTypeName);
        }
    
        private ArraySqlValue(final Object[] arr, final String dbTypeName) {
            this.arr = checkNotNull(arr);
            this.dbTypeName = checkNotNull(dbTypeName);
        }
    
        @Override
        public void setValue(final PreparedStatement ps, final int paramIndex) throws SQLException {
            final Array arrayValue = ps.getConnection().createArrayOf(dbTypeName, arr);
            ps.setArray(paramIndex, arrayValue);
        }
    
        @Override
        public void cleanup() {}
    
        private static String determineDbTypeName(final Object[] arr) {
            // use Spring Utils similar to normal JdbcTemplate inner workings
            final int sqlParameterType =
                StatementCreatorUtils.javaTypeToSqlParameterType(arr.getClass().getComponentType());
            final JDBCType jdbcTypeToUse = JDBCType.valueOf(sqlParameterType);
            // lowercasing typename for Postgres
            final String typeNameToUse = jdbcTypeToUse.getName().toLowerCase(Locale.US);
            return typeNameToUse;
        }
    }
    

    此代码在公共领域提供

    【讨论】:

    • 有效。我必须将varchar 作为第二个参数传递,因为我的数组是varchar(10)[]
    【解决方案2】:
    private static final String ARRAY_DATATYPE = "int4";
    private static final String SQL_UPDATE = "UPDATE foo SET arr = ? WHERE d = ?";
    final Integer[] existing = ...;
    final DateTime dt = ...;
    
    getJdbcTemplate().update(new PreparedStatementCreator() {
        @Override
        public PreparedStatement createPreparedStatement(final Connection con) throws SQLException {
            final PreparedStatement ret = con.prepareStatement(SQL_UPDATE);
            ret.setArray(1, con.createArrayOf(ARRAY_DATATYPE, existing));
            ret.setDate(2, new java.sql.Date(dt.getMillis()));
            return ret;
        }
    });
    

    【讨论】:

    【解决方案3】:

    这个解决方案是一种使用 postgreSQL 内置函数的解决方法,它绝对对我有用。

    reference blog

    1) 将字符串数组转换为逗号分隔的字符串

    如果您使用的是 Java8,这很容易。其他选项是here

    String commaSeparatedString = String.join(",",stringArray); // Java8 feature
    

    2) PostgreSQL 内置函数 string_to_array()

    你可以找到其他的postgreSQL数组函数here

    // tableName ( name text, string_array_column_name text[] )
    
    String query = "insert into tableName(name,string_array_column_name ) values(?, string_to_array(?,',') )";
    
    
    int[] types = new int[] { Types.VARCHAR, Types.VARCHAR};
    
    Object[] psParams = new Object[] {"Dhruvil Thaker",commaSeparatedString };
    
    jdbcTemplate.batchUpdate(query, psParams ,types); // assuming you have jdbctemplate instance
    

    【讨论】:

    • 您的解决方案是基于假设,输入字符串中的逗号是不合法的,OP 没有说明这样的假设。为什么在发布好的解决方案 5 年后发布不好的解决方案?
    • 您的解决方案对我来说效果很好。非常感谢。因为我知道我的输入可能包含逗号,所以我选择使用“||”作为分隔符。
    【解决方案4】:

    到目前为止我发现的最简洁的方法是首先将Collection 转换为Integer[],然后使用Connection 将其转换为Array

    Integer[] idArray = ids.toArray(new Integer[0]);
    
    Array idSqlArray = jdbcTemplate.execute(
            (Connection c) -> c.createArrayOf(JDBCType.INTEGER.getName(), idArray)
    );
    
    update = this.jdbcTemplate.update(setUsersArrayQuery, new Object[] {
                                idSqlArray, idUser, idDevice })
    

    这是基于文档中的信息:https://jdbc.postgresql.org/documentation/head/arrays.html

    【讨论】:

      【解决方案5】:

      参数类型和参数不匹配。

      尝试更改参数类型顺序

      int[] tipi = new int[3];
      tipi[0] = java.sql.Types.ARRAY;
      tipi[1] = java.sql.Types.INTEGER;
      tipi[2] = java.sql.Types.INTEGER;
      

      或使用

      update = this.jdbcTemplate.update(setUsersArrayQuery, new Object[] {
                                      ids.toArray(), idUser, idDevice })
      

      看看有没有效果

      【讨论】:

      • 我编辑了我的答案,我尝试了你的两个解决方案,我想我需要一个 java.sql.Array 的实例,但是如何使用 JdbcTemplate 创建它?
      【解决方案6】:

      http://valgogtech.blogspot.com/2009/02/passing-arrays-to-postgresql-database.html 解释如何创建 java.sql.Array postgresql 基本上 Array.getBaseTypeName 应该返回 int 并且 Array.toString 应该返回 "{1,2,3}" 格式的数组内容

      创建数组后,您可以使用 preparedstatement.setArray(...) 对其进行设置 来自 PreparedStatementCreator 例如

      jdbcTemplate.update(
          new PreparedStatementCreator() {
              public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
      

      祝你好运..

      【讨论】:

        【解决方案7】:
        java.sql.Array intArray = connection.createArrayOf("int", existing);
        List<Object> values= new ArrayList<Object>();
        values.add(intArray);
        values.add(dt);
        getJdbcTemplate().update(SQL_UPDATE,values);
        

        【讨论】:

        • 它受到 4 岁以上答案的“启发”。您只需尝试复制它,做一些混搭,使其变得更糟(例如,您现在需要处理 SQLException,甚至没有在您的“代码”中显示)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-12
        相关资源
        最近更新 更多