去年我面临同样的问题。解决方案是使用内建的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的文档指出:
已生成保持在该ID服务器在每个连接的基础上。这意味着该函数返回给定客户端的值是为该客户端影响AUTO_INCREMENT列的最新语句生成的第一个AUTO_INCREMENT值。即使它们生成自己的AUTO_INCREMENT值,该值也不会受到其他客户端的影响。此行为可确保每个客户端都可以检索自己的ID,而不必关心其他客户端的活动,而无需锁定或事务。
编辑:
给出here:
没有参数,LAST_INSERT_ID()返回表示成功插入用于AUTO_INCREMENT列作为结果,第一自动生成的值的64位的值最近执行的INSERT语句。
因此,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行:
和4行中的表2每一个适当地与键表1相关联:
因此,关键点是在插入带有所需的生成密钥的新行之后调用LAST_INSERT_ID()。
说实话我总是发现mysqlcpp的界面古老而笨重。我直接返回使用mysql c库。 –
从我的程序中的注释:\t对于自动递增ID字段,需要加载ID字段。 SQL函数LAST_STATEMENT_ID()可能不会返回正确的值,特别是如果记录未更新或插入未导致新(增加)的记录标识。获得更新ID的唯一安全可靠的方法是从表中重新加载ID字段。 –
@RichardHodges,是否可以使用c API检索密钥? – r0ng