Spring:如何在 PostgreSQL 中使用 KeyHolder

2022-09-01 23:04:44

最近迁移到POSTGRESQL,我正在尝试在db表中创建新条目时获取唯一生成的密钥。该表如下所示:screenstable

CREATE TABLE screenstable
(
  id serial NOT NULL,
  screenshot bytea,
  CONSTRAINT screen_id PRIMARY KEY (id )
)

将数据插入其中的方法如下:screenstable

@Autowired NamedParameterJDBCTemplate template;
public int insertImage(ImageBean imageBean){
        String query = "insert into screenstable (screenshot) values (:image)";
        SqlParameterSource data = new BeanPropertySqlParameterSource(imageBean);
        KeyHolder keyHolder = new GeneratedKeyHolder();
        template.update(query, data, keyHolder);
        return keyHolder.getKey().intValue();
    }

并且是ImageBean

import java.util.Arrays;

public class ImageBean {
    private int id;
    private byte[] image;
    @Override
    public String toString() {
        return "ImageBean [id=" + id + ", image=" + Arrays.toString(image)
                + "]";
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public byte[] getImage() {
        return image;
    }
    public void setImage(byte[] image) {
        this.image = image;
    }
}

但是运行代码会给出以下异常

15:33:20,953 ERROR JsonParseExceptionMapper:15 - org.springframework.dao.InvalidDataAccessApiUsageException: The getKey method should only be used when a single key is returned.  The current key entry contains multiple keys: [{id=3, screenshot=[B@db59df}]
org.springframework.dao.InvalidDataAccessApiUsageException: The getKey method should only be used when a single key is returned.  The current key entry contains multiple keys: [{id=3, screenshot=[B@db59df}]
        at org.springframework.jdbc.support.GeneratedKeyHolder.getKey(GeneratedKeyHolder.java:65)
        at some.project.model.FeedbackDao.insertImage(FeedbackDao.java:20)
        at some.project.rest.FeedsRest.pluginCheck(FeedsRest.java:62)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597).....

用于在以下情况下运行良好的相同代码,但在与POSTGRES一起使用时,键会失败。数据类型是导致代码失败的原因,还是我正确使用了主键功能?MySQLserial

请指教。


答案 1

如果框架没有被告知哪一列是键,它将返回表的所有列作为键。

您可以通过将新参数传递给 update 方法来通知它,如下所示:

template.update(query, data, keyHolder, new String[] { "id" });

See NamedParameterJdbcTemplate.update(sql, paramSource, generatedKeyHolder, keyColumnNames)


答案 2

您也可以继续使用JdbcTemplate,但在这种情况下,您必须检查它:

    jdbcTemplate.update(this, holder);
    Long newId;
    if (holder.getKeys().size() > 1) {
        newId = (Long)holder.getKeys().get("your_id_column");
    } else {
        newId= holder.getKey().longValue();
    }

GenerateKeyHolder 类引发异常,因为该类只知道当有一个键时应该返回什么(并且该键必须是 Number 实例,因为 getKey 方法返回 Number 对象)。

在jdbc驱动程序返回多个键的情况下,您必须确定生成的键列(更新之前或之后)。请看一下生成的KeyHolder#getKey的源代码 - 分析起来非常简单。检查密钥列表大小,并且具有多个密钥的Spring不知道应该返回哪个密钥,这就是返回异常的原因。

注意:请记住,这种方法不适用于Oracle,因为Oracle返回类似ROWID的东西。使用Oracle,您必须使用NameParameterJdbcTemplate。


推荐