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

标签 c++ mysql

我正在使用 mysql C++ 连接器。我有一张 table :

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++ 连接器中的函数?

例如,在 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/en/connector-j-usagenotes-last-insert-id.html

有任何 C++ 连接器替代方案吗?

谢谢

最佳答案

去年我遇到了同样的问题。解决方案是使用内置的 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

关于调用 LAST_INSERT_ID() 的安全性,如 mysql 文档所述:

The ID that was generated is maintained in the server on a per-connection basis. This means that the value returned by the function to a given client is the first AUTO_INCREMENT value generated for most recent statement affecting an AUTO_INCREMENT column by that client. This value cannot be affected by other clients, even if they generate AUTO_INCREMENT values of their own. This behavior ensures that each client can retrieve its own ID without concern for the activity of other clients, and without the need for locks or transactions.

编辑:

如给定here :

With no argument, LAST_INSERT_ID() returns a 64-bit value representing the first automatically generated value successfully inserted for an AUTO_INCREMENT column as a result of the most recently executed INSERT statement.

因此,LAST_INSERT_ID 返回最后生成的 ID,而不管新行插入到哪个表。如果您需要插入多行,只需在插入每一行后立即调用 LAST_INSERT_ID,这样您就可以获取 key 。

在下面的代码中,它在表 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()。

关于c++ - MySQL C++ 连接器如何从插入查询中检索自动递增键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46800465/

相关文章:

php - mysql 语句不起作用

c++ - 编译(?)问题。 Visual Studio C++ 2008

c++ - 在 L1/L2 中快速合并 4K float 的排序子集

mysql - 在单个 MySQL 查询中获取多个值

mysql - 使用 Sequelize 计算关联条目

MYSQL-无法计算字段之间的时间差并且无法使用 where 子句

mysql - 识别关系 - 多对多

c++ - 为什么在 C++ 中使用 shell 链接查找快捷方式的目标路径时它指的是 windows\installer 文件夹

c++ - OpenCV OpenCL 线程安全 - 死锁(将 cv::Mat 更改为 UMat)

C++ 崩溃(编译器?)