MySQL C++连接器如何从插入查询中检索自动增量键

3

我是使用mysql C++连接器。我有一张表:

CREATE TABLE some_table 
(
    id INT NOT NULL AUTO_INCREMENT, 
    col1 INT, 
    col2 INT,
    PRIMARY KEY ( id )
);

为了在查询中插入多条记录,我使用以下代码:
INSERT INTO some_table
    (col1, col2)
VALUES
    (0, 1),
    (2, 3),
    (4, 5);

我的问题是:在插入之后,我想检索所有自动生成的ID。是否可能在不创建另一个查询的情况下使用C++连接器中的函数?请注意保留HTML标记。

例如,在JDBC中,可以通过以下方法检索AUTO_INCREMENT列值:

stmt.executeUpdate(
        "INSERT INTO autoIncTutorial (dataField) "
        + "values ('Can I Get the Auto Increment Field?')",
        Statement.RETURN_GENERATED_KEYS);

//
// Example of using Statement.getGeneratedKeys()
// to retrieve the value of an auto-increment
// value
//

int autoIncKeyFromApi = -1;

rs = stmt.getGeneratedKeys();

if (rs.next()) {
    autoIncKeyFromApi = rs.getInt(1);
} else {

    // throw an exception from here
}

https://dev.mysql.com/doc/connector-j/5.1/zh/connector-j-usagenotes-last-insert-id.html

有没有其他的C++连接器替代品?

谢谢。


说实话,我一直觉得mysqlcpp的界面古老而笨重。我又回来直接使用mysql c库了。 - Richard Hodges
从我程序的注释中得知,对于自增ID字段,需要加载ID字段。SQL函数LAST_STATEMENT_ID()可能不会返回正确的值,特别是如果记录没有被更新或插入没有导致新的(递增的)记录ID。获取更新后的ID的唯一安全、可靠的方法是重新加载表格中的ID字段。 - Thomas Matthews
@RichardHodges,可以使用C API检索密钥吗? - r0n9
1个回答

1
去年我遇到了同样的问题。解决方法是使用内置函数LAST_INSERT_ID()。接下来,我将getting start example 2进行了更改以展示如何使用它:
    //previous variable declarations and initialisation similar to the original example
    driver = get_driver_instance();
    con = driver->connect("tcp://127.0.0.1:3306", "root", "root");
    con->setSchema("test_schema");

    con->setAutoCommit(false);

    stmt = con->createStatement();
    stmt->execute("DROP TABLE IF EXISTS tbl__test1");
    stmt->execute("DROP TABLE IF EXISTS tbl_test2");

    const string createTbl1Statement = "CREATE TABLE `tbl__test1` ("
            "`id` int(11) NOT NULL AUTO_INCREMENT,"
            "`col_value` varchar(45) DEFAULT NULL,"
            "PRIMARY KEY (`id`)"
            ") ENGINE=InnoDB DEFAULT CHARSET=latin1;";

    const string createTbl2Statement = "CREATE TABLE `tbl_test2` ("
            "`id` int(11) NOT NULL AUTO_INCREMENT,"
            "`tbl_test1_id` int(11) NOT NULL,"
            "`col_value` varchar(45) DEFAULT NULL,"
            "PRIMARY KEY (`id`)"
            ") ENGINE=InnoDB DEFAULT CHARSET=latin1;";

    stmt->execute(createTbl1Statement);
    stmt->execute(createTbl2Statement);

    pstmt = con->prepareStatement(
            "INSERT INTO tbl__test1(col_value) VALUES ('abcde')");
    pstmt->executeUpdate();
    delete pstmt;

    stmt->execute("SET @lastInsertId = LAST_INSERT_ID()");
    delete stmt;

    const string insertTbl2 = "INSERT INTO tbl_test2(tbl_test1_id, col_value)" 
            " VALUES (@lastInsertId, '1234')";

    pstmt = con->prepareStatement(insertTbl2);
    pstmt->executeUpdate();
    delete pstmt;

    con->commit();

    delete con;
    //remain code is like the example 2 from mysql site

关于mysql文档中提到的call LAST_INSERT_ID()的安全性:

生成的ID在服务器上以每个连接为基础进行维护。这意味着函数返回给指定客户端的值是该客户端最近影响AUTO_INCREMENT列的大多数最新语句生成的第一个AUTO_INCREMENT值。即使其他客户端生成自己的AUTO_INCREMENT值,该值也不会受到影响。此行为确保每个客户端都可以检索其自己的ID,而无需担心其他客户端的活动,也无需锁定或事务。

编辑:

此处所述:

如果没有参数,LAST_INSERT_ID()将返回一个64位值,表示由于最近执行的INSERT语句而成功插入AUTO_INCREMENT列的第一个自动生成的值。

因此,LAST_INSERT_ID返回最后生成的id,而不管新行插入的表格。如果您需要插入多行,请在每次插入行后立即调用LAST_INSERT_ID,以便获取密钥。

在下面的代码中,向表1插入1行,获取生成的键(返回“1”),然后使用该键插入与之关联的表2中的2行新闻。然后再次向表1插入1行,再次获取生成的键(返回“2”),然后再次向表2中插入2行新闻:
#include <stdlib.h>
#include <iostream>

#include "mysql_connection.h"

#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>

using namespace std;

int main(void) {
    cout << endl;
    cout << "Let's have MySQL count from 10 to 1..." << endl;

    try {
        sql::Driver *driver;
        sql::Connection *con;
        sql::Statement *stmt;
        sql::PreparedStatement *pstmt1;
        sql::PreparedStatement *pstmt2;

        driver = get_driver_instance();
        con = driver->connect("tcp://127.0.0.1:3306", "root", "root");
        con->setSchema("test_schema");

        con->setAutoCommit(false);

        stmt = con->createStatement();
        stmt->execute("DROP TABLE IF EXISTS tbl__test1");
        stmt->execute("DROP TABLE IF EXISTS tbl_test2");

        const string createTbl1Statement = "CREATE TABLE `tbl__test1` ("
            "`id` int(11) NOT NULL AUTO_INCREMENT,"
            "`col_value` varchar(45) DEFAULT NULL,"
            "PRIMARY KEY (`id`)"
            ") ENGINE=InnoDB DEFAULT CHARSET=latin1;";

        const string createTbl2Statement = "CREATE TABLE `tbl_test2` ("
            "`id` int(11) NOT NULL AUTO_INCREMENT,"
            "`tbl_test1_id` int(11) NOT NULL,"
            "`col_value` varchar(45) DEFAULT NULL,"
            "PRIMARY KEY (`id`)"
            ") ENGINE=InnoDB DEFAULT CHARSET=latin1;";

        stmt->execute(createTbl1Statement);
        stmt->execute(createTbl2Statement);

        pstmt1 = con->prepareStatement(
            "INSERT INTO tbl__test1(col_value) VALUES (?)");

        pstmt1->setString(1, "abcde");
        pstmt1->executeUpdate();

        stmt->execute("SET @lastInsertId = LAST_INSERT_ID()");

        const string insertTbl2 =
            "INSERT INTO tbl_test2(tbl_test1_id, col_value)"
                    " VALUES (@lastInsertId, ?)";
        pstmt2 = con->prepareStatement(insertTbl2);

        pstmt2->setString(1, "child value 1");
        pstmt2->executeUpdate();

        pstmt2->setString(1, "child value 2");
        pstmt2->executeUpdate();

        pstmt1->setString(1, "xpto");
        pstmt1->executeUpdate();

        stmt->execute("SET @lastInsertId = LAST_INSERT_ID()");

        pstmt2->setString(1, "child value 3");
        pstmt2->executeUpdate();

        pstmt2->setString(1, "child value 4");
        pstmt2->executeUpdate();

        con->commit();

        delete stmt;
        delete pstmt1;
        delete pstmt2;

        delete con;

    } catch (sql::SQLException &e) {
        cout << "# ERR: SQLException in " << __FILE__;
        cout << "(" << __FUNCTION__ << ") on line " << __LINE__ << endl;
        cout << "# ERR: " << e.what();
        cout << " (MySQL error code: " << e.getErrorCode();
        cout << ", SQLState: " << e.getSQLState() << " )" << endl;
    }

    cout << endl;

    return EXIT_SUCCESS;
}

结果是表格1中的2行:

2 rows inserted in table 1

在表格2中有4行,每一行都与表格1中的关键字正确关联:

4 rows inserted in table 2

因此,关键点是在插入具有所需生成键的新行后调用LAST_INSERT_ID()。

感谢您的回答,在您的回答中,插入了一条记录。我正在尝试插入多条记录。根据我的理解,查询LAST_INSERT_ID()只会返回1个ID,是这样吗? - r0n9
{btsdaf} - Duloren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接