Statement.RETURN_GENERATED_KEYS是否会生成任何额外的往返以获取新创建的标识符?

时间:2016-06-24 05:37:21

标签: mysql sql-server oracle postgresql jdbc

JDBC允许我们使用以下语法获取数据库自动生成的主键值(例如IDENTITYAUTO_INCREMENT):

PreparedStatement ps= connection.prepareStatement(
    "INSERT INTO post (title) VALUES (?)",
     Statement.RETURN_GENERATED_KEYS
);

while (resultSet.next()) {
    LOGGER.info("Generated identifier: {}", resultSet.getLong(1));
}

我很感兴趣Oracle,SQL Server,postgresQL或MySQL驱动程序是否使用单独的往返来获取标识符,或者只有一次往返执行插入并自动获取ResultSet

5 个答案:

答案 0 :(得分:3)

这取决于数据库和驱动程序。

虽然你没有要求它,但我会回答Firebird;)。在Firebird / Jaybird中,检索本身并不需要额外的往返,但使用Statement.RETURN_GENERATED_KEYS或整数数组版本将需要三次额外的往返(准备,执行,获取)来确定要请求的列(我仍然需要为它构建一种缓存形式)。使用带有String数组的版本不需要额外的往返(我希望在PostgreSQL中有RETURNING * ...)。

答案 1 :(得分:2)

在带有PgJDBC的PostgreSQL中,没有额外的往返来获取生成的密钥。

它发送Parse / Describe / Bind / Execute消息系列,然后发送Sync,然后读取结果,包括返回的结果集。由于协议管道请求,因此只需要一个客户端/服务器往返。

但是,有时可以将流式传输到服务器的批次分解为更小的块,或者如果请求生成的密钥则依次运行。要避免这种情况,请使用String[]数组表单来命名要返回的列,并仅命名固定宽度数据类型的列,如integer。这仅适用于批次和it's a due to a design problem in PgJDBC

(我发布了没有该限制的a patch to add batch pipelining support in libpq,它会为任意大小的批次执行一次客户端/服务器往返,包括返回密钥。)

答案 2 :(得分:1)

MySQL响应执行语句,在协议的OK数据包中自动接收生成的密钥。请求生成的密钥时没有通信开销。

答案 3 :(得分:0)

根据框架或库来执行在纯SQL中完全可能的事情是糟糕的设计恕我直言,特别是在对定义的DBMS工作时。 (Statement.RETURN_GENERATED_KEYS相对无害,虽然它显然确实为你提出了一个问题,但是框架是建立在单独的实体上并在代码中进行各种连接和过滤或者使用定制的事务隔离逻辑时效率低下很快就弄乱了。)

为什么不简单:

PreparedStatement ps= connection.prepareStatement(
    "INSERT INTO post (title) VALUES (?) RETURNING id");

单程,定义结果。

答案 4 :(得分:0)

在我看来,即使对于这样一个微不足道的事情,在所有数据库系统中工作的单一方法也会失败。 唯一务实的解决方案(类似于Hibernate)为每个目标RDBMS找到最佳工作解决方案(和 称之为 dialect 的所有解决方案:)

此处为Oracle的信息

我使用序列生成密钥,IDENTITY列会观察到相同的行为。

create  table auto_pk
(id number,
pad varchar2(100));

这可以使用仅一次往返

def stmt = con.prepareStatement("insert into auto_pk  values(auto_pk_seq.nextval, 'XXX')",
Statement.RETURN_GENERATED_KEYS) 

def rowCount = stmt.executeUpdate()

def generatedKeys = stmt.getGeneratedKeys()

if (null != generatedKeys && generatedKeys.next()) {
def   id = generatedKeys.getString(1);

但不幸的是,你得到 ROWID - 不是生成的密钥

如何在内部实施?如果激活10046跟踪,您可以看到它(BTW这也是最好的方法 进行了多少往返)

PARSING IN CURSOR 
insert into auto_pk values(auto_pk_seq.nextval, 'XXX') 
RETURNING ROWID INTO :1 
END OF STMT

因此,您看到 JDBC Standard 3.0已实施,但您未获得请求的结果。在封面下使用了 RETURNING条款。

因此,在Oracle中获取生成密钥的正确方法是:

def stmt = con.prepareStatement("insert into auto_pk values(auto_pk_seq.nextval, 'XXX') returning id into ?") 

stmt.registerReturnParameter(1, Types.INTEGER);          

def rowCount = stmt.executeUpdate()

def generatedKeys = stmt.getReturnResultSet()

if (null != generatedKeys && generatedKeys.next()) {
     def   id = generatedKeys.getLong(1);
    } 

注意:Oracle Release 12.1.0.2.0

激活10046 trace使用

con.createStatement().execute "alter session set events '10046 trace name context forever, level 12'"
con.createStatement().execute "ALTER SESSION SET tracefile_identifier = my_identifier"