諸事情により、C++からMySQLを使わなければならなくなりました。
今後のためにも、手順などのメモを残しておきます。
動作環境
- OS : CentOS 6.5
- MySQL : 5.5.40
- MySQL Connector/C++ : 1.1.3
- g++ : 4.4.7
- Boost : 1.41
- cmake : 2.6.4
ドライバ(MySQL Connector/C++)のダウンロード
MySQL Serverへ接続するためのドライバは、下記よりダウンロードできます。
※ ドライバをダウンロードするにはOracleのアカウントが必要なので、事前に取得しておく必要があります。
今回はソースからビルドするので、Select Platformは"Source Code"を選択します。
そしてmysql-connector-c++-X.X.X.tar.gzをダウンロードします。
2014.12.26時点での最新バージョンは1.1.5ですが、私が使っていたのは1.1.3です。
以降のメモは1.1.3のものなので、1.1.5とは異なる部分が出てくると思いますが、ご了承下さい。
ドライバのビルド及びインストール
tar.gzファイルをダウンロードできたら、任意のディレクトリにコピーし、展開します。
展開が完了したらビルドを行うのですが、以下のパッケージがインストールされている必要があります。
- mysql-devel
- boost
- boost-devel
- cmake
INSTALL(展開後にできるテキストファイル)によると、boostは1.34.0以上、cmakeは2.6.2以上が必要ですが、
いずれもyum経由でインストールできます。
これからビルドを行いますが、インストールディレクトリを/usr/local/libから/usr/local/lib64に変更したいので、driver/CMakeLists.txtの以下を編集します(1.1.5では少し異なります)。
RUNTIME DESTINATION lib ⇒ RUNTIME DESTINATION lib64
ARCHIVE DESTINATION lib ⇒ ARCHIVE DESTINATION lib64
LIBRARY DESTINATION lib ⇒ LIBRARY DESTINATION lib64
ARCHIVE DESTINATION lib ⇒ ARCHIVE DESTINATION lib64
編集が完了したらビルドし、インストールします。
cd mysql-connector-c++-1.1.3 sudo cmake . -DBOOST_ROOT:STRING=/usr/lib64 sudo make sudo make install
問題なければ、/usr/local/includeにヘッダファイル、/usr/local/lib64にライブラリがインストールされます。
※ /usr/local直下に不要なテキストファイルがコピーされた場合、それらを削除します。
ライブラリパスの更新
プログラムからリンクする際、いちいちディレクトリを指定しなくてもいいようにライブラリパスの情報を更新します。
/etc/ld.so.conf.d以下にmysql-connector-cpp-x86_64.confを作成
/usr/local/lib64
ファイルを作ったら、ldconfig
コマンドを実行します。
MySQL Connector/C++のラッパークラス
以下のソースを参考にして作成しました。
https://github.com/eduardocasas/MySQL-Connector-Cpp-Wrapper-Class
#ifndef _MYSQLCONNWRAPPER_H_
#define _MYSQLCONNWRAPPER_H_
#include <vector>
#include <stdexcept>
#include <mysql_connection.h>
#include <mysql_driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>
class MySQLConnWrapper {
public:
MySQLConnWrapper(
const std::string host, const std::string dbName,
const std::string user, const std::string passwd);
virtual ~MySQLConnWrapper();
void manageException(sql::SQLException& e);
void connectDb();
void switchDb();
void closeDb();
void executeQuery(const std::string& sql);
void setPrepStmt(const std::string& query);
void executePrepStmt();
void setInt(const int& num, const int& data);
void setDouble(const int& num, const double& data);
void setString(const int& num, const std::string& data);
void setDateTime(const int& num, const std::string& data);
void setNull(const int& num);
void executeFetch(const std::string key, std::string& result);
void executeFetch(const std::string key, std::vector<std::string>& results);
void executeFetch(const std::vector<std::string>& keys, std::vector<std::string>& results);
void executeFetch(const std::vector<std::string>& keys, std::vector< std::vector<std::string> >& results);
private:
std::string _host;
std::string _dbName;
std::string _port;
std::string _user;
std::string _passwd;
sql::Driver* _driver;
sql::Connection* _con;
sql::Statement* _stmt;
sql::ResultSet* _res;
sql::PreparedStatement* _prepStmt;
};
#endif /*_MYSQLCONNWRAPPER_H_ */
#include "MySQLConnWrapper.h"
MySQLConnWrapper::MySQLConnWrapper(
const std::string host, const std::string dbName,
const std::string user, const std::string passwd){
_host = host;
_dbName = dbName;
_user = user;
_passwd = passwd;
}
MySQLConnWrapper::~MySQLConnWrapper(){
if(!_res) delete _res;
if(!_stmt) delete _stmt;
if(!_prepStmt) delete _prepStmt;
if(!_con) delete _con;
}
/**
* Create an error message related to SQL Exception
*/
void MySQLConnWrapper::manageException(sql::SQLException& e){
if(e.getErrorCode() != 0){
std::string msg = "SQL Exception : " + static_cast<std::string>(e.what());
msg += " (MySQL error code : " + e.getErrorCode();
msg += ", SQLState : " + e.getSQLState() + " )\n";
std::cerr << msg << std::endl;
}
}
/**
* Connect to mysql server
*/
void MySQLConnWrapper::connectDb(){
try{
_driver = sql::mysql::get_driver_instance();
_con = _driver->connect(_host, _user, _passwd);
}catch(sql::SQLException& e){
this->manageException(e);
throw;
}
}
/**
* Select a database
*/
void MySQLConnWrapper::switchDb(){
if(!_con){
std::string msg = std::string(__func__) + " Failed. ";
msg += "System is not still connected to the database.";
throw std::runtime_error(msg);
}
try{
_con->setSchema(_dbName);
_stmt = _con->createStatement();
}catch(sql::SQLException& e){
this->manageException(e);
throw;
}
}
/**
* Disconnect to mysql server
*/
void MySQLConnWrapper::closeDb(){
if(!_con){
std::string msg = std::string(__func__) + " Failed. ";
msg += "System is not still connected to the database.";
throw std::runtime_error(msg);
}
_con->close();
}
/**
* Execute a query directly
*/
void MySQLConnWrapper::executeQuery(const std::string& sql){
if(!_stmt){
std::string msg = std::string(__func__) + " Failed. ";
msg += "Statement is not set.";
throw std::runtime_error(msg);
}
try{
_res = _stmt->executeQuery(sql);
}catch(sql::SQLException& e){
this->manageException(e);
throw;
}
}
/**
* Set a prepared statement
*/
void MySQLConnWrapper::setPrepStmt(const std::string& query){
if(!_con){
std::string msg = std::string(__func__) + " Failed. ";
msg += "System is not still connected to the database.";
throw std::runtime_error(msg);
}
try{
_prepStmt = _con->prepareStatement(query);
}catch(sql::SQLException& e){
this->manageException(e);
throw;
}
}
/**
* Excecute a prepared statement
*/
void MySQLConnWrapper::executePrepStmt(){
if(!_stmt){
std::string msg = std::string(__func__) + " Failed. ";
msg += "Prepared Statement is not set.";
throw std::runtime_error(msg);
}
try{
_res = _prepStmt->executeQuery();
}catch(sql::SQLException& e){
this->manageException(e);
throw;
}
}
/**
* Fetch the result(using single key) and assign to a string variable
*/
void MySQLConnWrapper::executeFetch(const std::string key, std::string& result){
try{
if(_res->next()){
result = _res->getString(key);
}
}catch(sql::SQLException& e){
this->manageException(e);
throw;
}
}
/**
* Fetch the results(using single key) and assign to a string vector
*/
void MySQLConnWrapper::executeFetch(const std::string key, std::vector<std::string>& results){
try{
while(_res->next()){
results.push_back(_res->getString(key));
}
}catch(sql::SQLException& e){
this->manageException(e);
throw;
}
}
/**
* Fetch the results(using sevral keys) and assign to a string vector
*/
void MySQLConnWrapper::executeFetch(const std::vector<std::string>& keys,
std::vector<std::string>& results){
std::vector<std::string>::const_iterator it;
try{
if(_res->next()){
for(it = keys.begin(); it != keys.end(); it++){
results.push_back(_res->getString(*it));
}
}
}catch(sql::SQLException& e){
this->manageException(e);
throw;
}
}
/**
* Fetch the results(using sevral keys) and assign to string vectors
*/
void MySQLConnWrapper::executeFetch(const std::vector<std::string>& keys,
std::vector< std::vector<std::string> >& results){
std::vector<std::string>::const_iterator it;
try{
while(_res->next()){
std::vector<std::string> values;
for(it = keys.begin(); it != keys.end(); it++){
values.push_back(_res->getString(*it));
}
results.push_back(values);
}
}catch(sql::SQLException& e){
this->manageException(e);
throw;
}
}
/**
* Set a int value to a prepared statement
*/
void MySQLConnWrapper::setInt(const int& num, const int& data){
if(!_stmt){
std::string msg = std::string(__func__) + " Failed. ";
msg += "Prepared Statement is not set.";
throw std::runtime_error(msg);
}
_prepStmt->setInt(num, data);
}
/**
* Set a double value to a prepared statement
*/
void MySQLConnWrapper::setDouble(const int& num, const double& data){
if(!_stmt){
std::string msg = std::string(__func__) + " Failed. ";
msg += "Prepared Statement is not set.";
throw std::runtime_error(msg);
}
_prepStmt->setDouble(num, data);
}
/**
* Set a string value to a prepared statement
*/
void MySQLConnWrapper::setString(const int& num, const std::string& data){
if(!_stmt){
std::string msg = std::string(__func__) + " Failed. ";
msg += "Prepared Statement is not set.";
throw std::runtime_error(msg);
}
_prepStmt->setString(num, data);
}
/**
* Set a date value (data type is string) to a prepared statement
*/
void MySQLConnWrapper::setDateTime(const int& num, const std::string& data){
if(!_stmt){
std::string msg = std::string(__func__) + " Failed. ";
msg += "Prepared Statement is not set.";
throw std::runtime_error(msg);
}
_prepStmt->setDateTime(num, data);
}
/**
* Set a NULL value to a prepared statement
*/
void MySQLConnWrapper::setNull(const int& num){
if(!_stmt){
std::string msg = std::string(__func__) + " Failed. ";
msg += "Prepared Statement is not set.";
throw std::runtime_error(msg);
}
// The second argument of setNull is a int variable.
// However, since this variable is not used in mysqlcppconn, set a NULL.
_prepStmt->setNull(num, NULL);
}
SQLの実行は`executeQuery`か`executePrepStmt`を使う形になるため、ほぼSQL文を直接書きます。 ※ 使用用途が限られていたため、これで十分でした。
結果の取得はexecuteFetch
を使って、すべてstring
型で受け取るようにしています。
なので、値の型変換はこのクラスを使う側にて行う形になります。
executeFetch
としてオーバーロードされる関数は4つあり、それぞれ以下のように区別しています。
executeFetch(const std::string key, std::string& result)
key |
---|
value |
SELECT文の結果が、1つのkeyに対して1つのvalueとなる場合に使用
executeFetch(const std::string key, std::vector<std::string>& results)
key |
---|
value1 |
value2 |
value3 |
SELECT文の結果が、1つのkeyに対して複数のvalueとなる場合に使用
executeFetch(const std::vector<std::string>& keys, std::vector<std::string>& results)
key1 | key2 | key3 |
---|---|---|
value1 | value2 | value3 |
SELECT文の結果が、複数のkeyに対してそれぞれ1つのvalueとなる場合に使用
executeFetch(const std::vector<std::string>& keys, std::vector< std::vector<std::string> >& results)
key1 | key2 | key3 |
---|---|---|
value11 | value12 | value13 |
value21 | value22 | value23 |
value31 | value32 | value33 |
SELECT文の結果が、複数のkeyに対してそれぞれ複数のvalueとなる場合に使用
公開するのが恥ずかしいぐらいの実装かもしれないのですが、限られた用途だと意外に使いやすかったです。