此数据库不支持 getGeneratedKeys 功能

Posted

技术标签:

【中文标题】此数据库不支持 getGeneratedKeys 功能【英文标题】:getGeneratedKeys feature is not supported by this database 【发布时间】:2015-04-29 16:07:45 【问题描述】:

我正在运行 SQL Anywhere 16.0 并尝试使用 Spring JDBC 模板。我需要做的很简单:在表中插入一行,然后取回自动生成的 ID 值。

public int log() 
    SimpleJdbcInsert insertActor = 
            new SimpleJdbcInsert(ds)
            .withTableName("DBA.REQUESTS")
            .usingGeneratedKeyColumns("REQUEST_ID");

    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("USER_ID", userId);
    parameters.put("DATA_TYPE_ID", getProductSku());
    parameters.put("PRICE", price);
    // ...more parameters

    Number requestIdNumber = insertActor.executeAndReturnKey(parameters);
    return requestIdNumber.intValue();

但是 Spring 反复给我错误

org.springframework.dao.InvalidDataAccessResourceUsageException: 
  The getGeneratedKeys feature is not supported by this database

我使用的驱动应该支持 JDBC 4.0(库是 dbjdbc16.dll,它在路径中,sajdbc4.jar 在 Tomcat lib 目录中)。来自 Tomcat 的相关数据库连接信息是

    <Resource auth="Container" description="Pooled connection to the web database" 
    driverClassName="sybase.jdbc4.sqlanywhere.IDriver" 
    maxActive="30" maxIdle="5" maxWait="10000" name="jdbc/web" 
    removeAbandoned="true" 
    removeAbandonedTimeout="60" 
    type="javax.sql.DataSource" 
    url="jdbc:sqlanywhere:Server=web;UID=xxx;PASSWORD=xxx;port=xxxx;LINKS=tcpip(PORT=xxxx)"/>

数据源的 Spring 应用程序上下文是

<jee:jndi-lookup id="dbDataSource" jndi-name="jdbc/web"
            expected-type="javax.sql.DataSource" />  

所以我的问题是,有没有办法更好地配置东西,以便这种类型的语句起作用? 如果数据库在某些基本层面上确实不支持这一点,我是否有一个不难看的替代方法来插入值并取回生成的 id。

更新:似乎数据库驱动程序不支持此功能。此处和其他地方的建议是执行 INSERT 而不是紧随其后的 SELECT。当然,问题是如果另一个用户在这两个语句之间插入表中,您将得到错误的值,在这种情况下会非常糟糕。

我现在的解决方法是在相关 DAO 上使用类级锁定,并根据几列(不仅仅是 IDENTITY)进行选择,这样我就可以 99.9% 确定我得到了相同的行.工作就足够了。也就是说,我更喜欢有一种锁定表的事务方式。我不认为标记函数@Transactional 会为此工作,对吧,因为这只会延迟提交,直到所有语句都成功?

【问题讨论】:

this question 有帮助吗? 不,不幸的是,这正是我所做的。 您的驱动是否支持返回生成的密钥?这是 JDBC 4.0 规范中的一个可选特性。您可以通过使用DatabaseMetaData.supportsGetGeneratedKeys 方法检查JDBC 数据库元数据来确定它是否存在。 啊——可选功能——解释了它。该方法给出“否”作为答案,这就是 Spring 显示错误的原因。 【参考方案1】:

我根本不是 JDBC 专家,所以我无法直接回答这个问题,但我可以告诉您,SQL Anywhere 服务器确实具备这种能力。如果 JDBC 的东西不起作用,您可以使用 @@identity 变量。获取select @@identity,它将返回最后一条语句的自动生成值。

免责声明:我在 SAP 工作,从事 SQL Anywhere 工程。

【讨论】:

谢谢格雷姆!不幸的是,这不包括另一个用户在您运行选择之前执行了插入的情况。如果他们有,你会得到错误的值,在这种情况下,这将是非常糟糕的。如果有一种 (JDBC) 方法可以在 SELECT 完成之前锁定表,那将是理想的。 不,事实并非如此。抱歉,我的描述不完整 - select @@identity 返回最后一个插入语句在当前连接上完成 的自动生成值。因此,如果您的连接插入一行,那么另一个连接插入一行,您的连接上的select @@identity 返回插入的行 you 的 id。 非常有趣。不过,我使用了一个连接池——我认为,两个用户可能会共享一个连接,除非有某种方法可以防止这种情况发生?

以上是关于此数据库不支持 getGeneratedKeys 功能的主要内容,如果未能解决你的问题,请参考以下文章

getGeneratedKeys() 上的 Grails Hibernate4 升级错误

(转)通过getGeneratedKeys()获取主键

如何让 Statement.getGeneratedKeys 工作

sql jdbc getgeneratedkeys 返回列“id”未找到,列类型未知

在 MySQL 中使用 statement.getGeneratedKeys() 时是不是可以获得自动生成的值?

jdbc-mysql基础 ResultSet getGeneratedKeys 插入一条字段并返回其主键