PostgreSQL JDBC getGeneratedKeys返回所有列

时间:2013-11-04 11:31:29

标签: java postgresql jdbc

我最近从MySQL切换到PostgreSQL用于项目的后端,并发现了我需要检查的一些数据库代理方法。要插入链接对象,我使用事务来确保存储所有内容。我使用jdbc方法执行此操作,例如setAutoCommit(false)commit()。我编写了一个实用工具方法,将记录插入表中并返回生成的密钥。基本上我遵循了这里描述的技术2:

http://www.selikoff.net/2008/09/03/database-key-generation-in-java-applications/

这项工作从项目开始就有效,但在从MySQL迁移到PostgreSQL后getGeneratedKeys返回新插入记录的所有列(参见下面的控制台输出)。

代码:

final ResultSet keys = ps.getGeneratedKeys();
final ResultSetMetaData metaData = keys.getMetaData();
for (int j = 0; j < metaData.getColumnCount(); j++) {
    System.out.println("Col name: "+metaData.getColumnName(j+1));
}

输出:

Col name: pathstart
Col name: fk_id_c
Col name: xpathid
Col name: firstnodeisroot

表的数据库签名(从pgAdmin III自动生成的SQL):

CREATE TABLE configuration.configuration_xpath
(
  pathstart integer NOT NULL,
  fk_id_c integer NOT NULL,
  xpathid integer NOT NULL DEFAULT nextval('configuration.configuration_xpath_id_seq'::regclass),
  firstnodeisroot boolean NOT NULL DEFAULT false,
  CONSTRAINT configuration_xpath_pkey PRIMARY KEY (xpathid),
  CONSTRAINT configuration_fk FOREIGN KEY (fk_id_c)
      REFERENCES configuration.configuration (id_c) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
)

PK背后序列的数据库签名:

CREATE SEQUENCE configuration.configuration_xpath_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 242
  CACHE 1
  OWNED BY configuration.configuration_xpath.xpathid;

所以问题是,为什么getGeneratedKeys会返回所有列而不仅仅是生成的键?我在这里搜索并发现了其他有类似问题的人:

http://www.postgresql.org/message-id/004801cb7518$cbc632e0$635298a0$@pravdin@disi.unitn.it

但他们的问题尚未得到解答,只提供了建议的解决方法。

2 个答案:

答案 0 :(得分:7)

大多数驱动程序通过使用自动生成的列在查询末尾添加getGeneratedKeys() - 子句来支持RETURNING。 PostgreSQL返回所有字段,因为它有RETURNING *,只返回所有列。这意味着要返回生成的密钥,它不必查询系统表来确定要返回的列,这样可以节省网络往返(和查询时间)。

JDBC规范隐式允许这样做,因为它says

  

注意:如果未指定表示自动生成的键的列,则JDBC驱动程序实现将确定最能代表自动生成的键的列。

在行之间阅读,你可以说这可以说'我不知道,或者说工作太多,所以所有列最能代表自动生成的密钥'

另一个原因可能是很难确定哪些列是自动生成的,哪些不是(我不确定PostgreSQL是否属实)。例如,在Jaybird(我维护的Firebird的JDBC驱动程序)中,我们也返回所有列,因为在Firebird中无法确定哪些列是自动生成的(但我们确实需要在系统表中查询列名,因为Firebird会没有RETURNING *)。

因此,始终建议按列名称而不是按位置显式查询生成的键ResultSet

其他解决方案使用接受String[]int[]的备用方法显式指定要返回的列名或列位置(尽管我不是100%确定PostgreSQL驱动程序如何处理)

BTW:Oracle更糟糕了:默认情况下它返回行的ROW_ID,你需要使用一个单独的查询来获取该行的(生成的)值。

答案 1 :(得分:1)

更新 - 接受的答案(马克)正确解释了问题所在。我的解决方案也有效,但这只是因为我在重新创建表时首先添加了PK列。无论哪种方式,所有列都由getGeneratedKeys()返回。

经过一些研究后,我设法找到问题的可能原因。正如我之前所说,在开发软件项目期间,我已经从MySQL改为PostgreSQL。对于这次迁移,我采用了我加载到PostgreSQL中的SQL转储。除了迁移的表格之外,我还创建了一些新的表格(使用pgAdmin III中的GUI向导)。在仔细研究了两个表(一个是导入的,一个已创建的)之间的差异之后,我已经确定了两件事:

    来自MySQL转储的
  1. CREATE TABLE语句将PK转换为BIGINT NOT NULL,而不是SERIAL。这导致自动生成的PK不再正常工作,尽管在我提出这个问题之前我已经解决了这个问题。

  2. 我固定的表格&#39;通过添加一个新的序列并将其链接工作完全正常,但SQL生成代码(由原始问题中显示的pgAdmin III自动生成)与PostgreSQL本地生成的表格不同。

  3. 请注意,我的固定表工作(ed)完美:我可以插入记录,更新记录和执行连接...基本上可以做任何事情。主键自动生成,序列更新。但是,JDBC驱动程序(准确地说是psotgresql-9.2-1003.jdbc4.jar)无法返回我生成的密钥(尽管这些表是完全正常的)。

    为了说明迁移表和已创建表之间的区别,下面是我在迁移后添加的表的生成代码示例:

    CREATE TABLE configuration.configuration_xpathitem
    (
      xpathitemid serial NOT NULL,
      xpathid integer,
      fk_id_c integer,
      itemname text,
      index integer,
      CONSTRAINT pk_configuration_xpathitem PRIMARY KEY (xpathitemid),
      CONSTRAINT fk_configuration_xpathitem_configuration FOREIGN KEY (fk_id_c)
          REFERENCES configuration.configuration (id_c) MATCH SIMPLE
          ON UPDATE NO ACTION ON DELETE NO ACTION,
      CONSTRAINT fk_configuration_xpathitem_configuration_xpath FOREIGN KEY (xpathid)
          REFERENCES configuration.configuration_xpath (xpathid) MATCH SIMPLE
          ON UPDATE NO ACTION ON DELETE NO ACTION
    )
    

    您可以清楚地看到我的PK具有serial关键字,其中integer not null default ...表示已迁移(和已修复)的表格。

    因此,我想可能PostgreSQL的JDBC驱动程序无法找到PK。我已经阅读了@Mark在他的回复中突出显示的规范,这让我认为这是驱动程序返回所有列的原因。这让我相信驱动程序找不到PK,因为我认为它正在寻找serial关键字。

    因此,为了解决这个问题,我转储了我的数据,删除了我的错误表并再次添加它们,这次是从头开始而不是使用MySQL转储中的SQL语句,并重新加载我的数据。这解决了我的问题。我希望这可以帮助那些也被困住的人。