去年我面臨同樣的問題。解決方案是使用內建的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