cocos2dx集成MySQL调用存储过程来实现管理后台(通过MySQL的Connector C++实现)

MySQL C++ Driver的实现基于JDBC规范

MySQL Connector/C++是由Sun Microsystems开发的MySQL连接器。它提供了基于OO的编程接口与数据库驱动来操作MySQL服务器。

与许多其他现存的C++接口实现不同,Connector/C++遵循了JDBC规范。也就是说,Connector/C++ Driver的API主要是基于Java语言的JDBC接口。JDBC是java语言与各种数据库连接的标准工业接口。

Connector/C++实现了大部分JDBC规范。如果C++程序的开发者很熟悉JDBC编程,将很快的入门。

MySQL Connector/C++需要安装配置boost库,boost库安装编译在这里不进行阐述


库下载:

mysql-connector-c++-8.0.18-win32库

boost_1_72_0库

工程配置:

  • 新建一个空项目ManageSystem:

  • 将下载好的mysql-connector-c++-8.0.18-win32和boost_1_72_0库放到对应的目录下,方便引用:

  • 将F:\Project\ManageSystem2\Third\mysql-connector-c++-8.0.18-win32\lib添加到项目的库目录中(根据具体路径而定)

  • 将F:\Project\ManageSystem2\Third\mysql-connector-c++-8.0.18-win32\include添加到VC++的包含目录中(根据具体路径而定),

  • 将F:\boost\boost_1_55_0添加到VC++的包含目录中(根据具体路径而定)

配置同上

  • 添加mysqlcppconn.lib至链接器--->输入--->附加依赖项中

同样,如果使用的mysql是64位的,还需要将项目的解决方案平台由win32改成x64

  • 将F:\Project\ManageSystem2\Third\mysql-connector-c++-8.0.18-win32\lib(根据具体路径而定)下的mysqlcppconn.dll复制到项目中去,和.cpp,.h文件位于同一路径下
  • 将F:\MySQL\MySQL Server 5.6\lib(根据具体路径而定)下的libmysql.dll复制到项目中去,和.cpp,.h文件位于同一路径下(配置完成)

或者放到项目的运行目录F:\Project\ManageSystem2\proj.win32\Debug.win32下面都可以。

程序实现:

  • 程序引入头文件
#include "jdbc/mysql_connection.h"
#include "jdbc/mysql_driver.h"
#include "jdbc/cppconn/statement.h"

using namespace sql;
using namespace std;

#define DBHOST "tcp://localhost:3306"
#define USER "root"
#define PASSWORD "123456"
  • 连接数据库,实现查询操作


	//初始化驱动
	//Driver *m_driver;
	//m_driver = get_driver_instance();

	sql::mysql::MySQL_Driver *driver = NULL;
	sql::Connection *conn = NULL;
	sql::Statement *state;
	sql::ResultSet *result;

	driver = sql::mysql::get_mysql_driver_instance();
	if (driver == NULL)
	{
		CCLOG("driver is null...");
	}

	conn = driver->connect(DBHOST, USER, PASSWORD);
	if (conn == NULL)
	{
		CCLOG("conn is null...");
	}
	else
	{
		CCLOG("connect suceess...");
	}
	CCLOG("-------------------------begin query------------------------------------------");

	state = conn->createStatement();
	state->execute("use db_babykylin");
	result = state->executeQuery("select * from t_users");
	//// 输出查询  
	while (result->next() != NULL)
	{
		//CCLOG("userid:%d", result->getInt("userid"));
		//CCLOG("account:%s", (result->getString("account")).c_str());

		CCLOG("userid:%s, account:%s, name:%s, sex:%s, headimg:%s, lv:%s, coins:%s, gems:%s, roomid:%s", (result->getString("userid")).c_str(), \
			(result->getString("account")).c_str(), (result->getString("name")).c_str(), (result->getString("sex")).c_str(), (result->getString("headimg")).c_str(), \
			(result->getString("lv")).c_str(), (result->getString("coins")).c_str(), (result->getString("gems")).c_str(), (result->getString("roomid")).c_str() );
	}
	CCLOG("--------------------------end query-----------------------------------------");

	delete conn;
	driver = NULL;
	//m_driver = NULL;
	conn = NULL;

数据库表结构:

查询结果:

执行update操作的存储过程:

/*
如果你事先并不知道SQL语句是SELECT还是INSERT,UPDATE或DELETE,你可以使用execute函数。
当SQL语句是SELECT操作时,execute()返回true,当SQL语句是INSERT,UPDATE,DELETE操作时,execute()返回false。
如果语句是SELECT查询操作,你可以调用Statement实例中的getResultSet函数获取查询结果集。
如果语句是INSERT,UPDATE,DELETE操作,你可以调用getUpdateCount()获取受影响的行数。
*/

	//--------执行存储过程-update---------
	int userid = 9;
	int coins = 100;
	int gems = 20;
	
	char proc[1024] = { 0 };
	sprintf(proc, "call update_user_info(%d,%d,%d)", userid, coins, gems);
	CCLOG("call proc:%s", proc);
	bool resExec = state->execute(proc);
	int uCount = 0;
	if (!resExec)
	{
		uCount = state->getUpdateCount();
	}
	CCLOG("call proc-update res:%d", uCount);







//--------存储过程---------
CREATE DEFINER=`root`@`localhost` PROCEDURE `update_user_info`(in_userid int,in_coins int,in_gems int)
BEGIN
	
	update t_users set coins=coins+in_coins,gems=gems+in_gems where userid=in_userid;

END

执行结果:

执行insert操作的存储过程:

/*
如果你事先并不知道SQL语句是SELECT还是INSERT,UPDATE或DELETE,你可以使用execute函数。
当SQL语句是SELECT操作时,execute()返回true,当SQL语句是INSERT,UPDATE,DELETE操作时,execute()返回false。
如果语句是SELECT查询操作,你可以调用Statement实例中的getResultSet函数获取查询结果集。
如果语句是INSERT,UPDATE,DELETE操作,你可以调用getUpdateCount()获取受影响的行数。
*/

	//--------执行存储过程-insert---------
	string name = "user5";
	string pwd = "123456";

	sprintf(proc, "call insert_manager('%s','%s')", name.c_str(), pwd.c_str());
	CCLOG("call proc:%s", proc);
	resExec = state->execute(proc);
	uCount = 0;
	if (!resExec)
	{
		uCount = state->getUpdateCount();
	}
	CCLOG("call proc-insert res:%d", uCount);







//--------存储过程---------
CREATE DEFINER=`root`@`localhost` PROCEDURE `insert_manager`(IN `in_name` varchar(64),IN `in_pwd` varchar(32))
BEGIN
	#Routine body goes here...
	INSERT INTO t_manager(name, pwd) VALUES(in_name, in_pwd);

END

执行结果:

数据库里面实现了插入。

执行delete操作的存储过程:

/*
如果你事先并不知道SQL语句是SELECT还是INSERT,UPDATE或DELETE,你可以使用execute函数。
当SQL语句是SELECT操作时,execute()返回true,当SQL语句是INSERT,UPDATE,DELETE操作时,execute()返回false。
如果语句是SELECT查询操作,你可以调用Statement实例中的getResultSet函数获取查询结果集。
如果语句是INSERT,UPDATE,DELETE操作,你可以调用getUpdateCount()获取受影响的行数。
*/

	//--------执行存储过程-delete---------
	name = "user7";

	sprintf(proc, "call delete_manager('%s')", name.c_str());
	CCLOG("call proc:%s", proc);
	resExec = state->execute(proc);
	uCount = 0;
	if (!resExec)
	{
		uCount = state->getUpdateCount();
	}
	CCLOG("call proc-delete res:%d", uCount);







//--------存储过程---------
CREATE DEFINER=`root`@`localhost` PROCEDURE `delete_manager`(IN `in_name` varchar(64))
BEGIN
	#Routine body goes here...
	DELETE FROM t_manager WHERE name=in_name;

END

因为没有这行记录,操作应该返回时0;

执行结果:

下面将name = "user6";改成这个,应该删除id为7的行;res应该返回非0值.

执行结果:

数据库里面已经删掉。


完整代码:

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"

#include "jdbc/mysql_connection.h"
#include "jdbc/mysql_driver.h"
#include "jdbc/cppconn/statement.h"

using namespace sql;
using namespace std;

#define DBHOST "tcp://localhost:3306"
#define USER "root"
#define PASSWORD "123456"

class HelloWorld : public cocos2d::Scene
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
};

#endif // __HELLOWORLD_SCENE_H__
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    return HelloWorld::create();
}

// Print useful error message instead of segfaulting when files are not there.
static void problemLoading(const char* filename)
{
    printf("Error while loading: %s\n", filename);
    printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in HelloWorldScene.cpp\n");
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }

    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();


	//初始化驱动
	//Driver *m_driver;
	//m_driver = get_driver_instance();

	sql::mysql::MySQL_Driver *driver = NULL;
	sql::Connection *conn = NULL;
	sql::Statement *state;
	sql::ResultSet *result;

	driver = sql::mysql::get_mysql_driver_instance();
	if (driver == NULL)
	{
		CCLOG("driver is null...");
	}

	conn = driver->connect(DBHOST, USER, PASSWORD);
	if (conn == NULL)
	{
		CCLOG("conn is null...");
	}
	else
	{
		CCLOG("connect suceess...");
	}
	CCLOG("-------------------------begin query------------------------------------------");

	state = conn->createStatement();
	state->execute("use db_babykylin");
	result = state->executeQuery("select * from t_users");
	//// 输出查询  
	while (result->next() != NULL)
	{
		//CCLOG("userid:%d", result->getInt("userid"));
		//CCLOG("account:%d", (result->getString("account")).c_str());

		CCLOG("userid:%s, account:%s, name:%s, sex:%s, headimg:%s, lv:%s, coins:%s, gems:%s, roomid:%s", (result->getString("userid")).c_str(), \
			(result->getString("account")).c_str(), (result->getString("name")).c_str(), (result->getString("sex")).c_str(), (result->getString("headimg")).c_str(), \
			(result->getString("lv")).c_str(), (result->getString("coins")).c_str(), (result->getString("gems")).c_str(), (result->getString("roomid")).c_str() );
	}
	CCLOG("--------------------------end query-----------------------------------------");

	//--------执行存储过程-update---------
	int userid = 9;
	int coins = 100;
	int gems = 20;
	
	char proc[1024] = { 0 };
	sprintf(proc, "call update_user_info(%d,%d,%d)", userid, coins, gems);
	CCLOG("call proc:%s", proc);
	bool resExec = state->execute(proc);
	int uCount = 0;
	if (!resExec)
	{
		uCount = state->getUpdateCount();
	}
	CCLOG("call proc-update res:%d", uCount);

	//--------执行存储过程-insert---------
	string name = "user6";
	string pwd = "123456";

	sprintf(proc, "call insert_manager('%s','%s')", name.c_str(), pwd.c_str());
	CCLOG("call proc:%s", proc);
	resExec = state->execute(proc);
	uCount = 0;
	if (!resExec)
	{
		uCount = state->getUpdateCount();
	}
	CCLOG("call proc-insert res:%d", uCount);

	//--------执行存储过程-delete---------
	name = "user6";

	sprintf(proc, "call delete_manager('%s')", name.c_str());
	CCLOG("call proc:%s", proc);
	resExec = state->execute(proc);
	uCount = 0;
	if (!resExec)
	{
		uCount = state->getUpdateCount();
	}
	CCLOG("call proc-delete res:%d", uCount);

	delete state;
	delete result;
	delete conn;
	driver = NULL;
	//m_driver = NULL;
	conn = NULL;

    return true;
}


void HelloWorld::menuCloseCallback(Ref* pSender)
{
    //Close the cocos2d-x game scene and quit the application
    Director::getInstance()->end();

    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif

    /*To navigate back to native iOS screen(if present) without quitting the application  ,do not use Director::getInstance()->end() and exit(0) as given above,instead trigger a custom event created in RootViewController.mm as below*/

    //EventCustom customEndEvent("game_scene_close_event");
    //_eventDispatcher->dispatchEvent(&customEndEvent);


}

 


参考文档:

  1. 官方手册: http://dev.mysql.com/doc/refman/5.6/en/connector-cpp.html
  2. MySQL Connector/C++文档翻译: http://www.cnblogs.com/dvwei/archive/2013/04/18/3029464.html

 

 

获取一个Statement对象

当调用Connection::createStatement函数时,返回一个Statement对象,它可以用来向数据库服务器发送SQL语句。一般情况下,Statement对象执行的是不带参数的SQL语句。换句话说,一个Statement对象用于执行静态SQL语句,并返回其执行结果。如果需要执行多次不同输入的SQL语句,可以考虑使用Prepared Statements对象。

执行SQL语句

在执行SQL语句前,你需要先连接数据库,并且选择相应的数据库。通过调用Connection对象中的setSchema函数来选择需要连接的架构(待连接的数据库名称),setSchema函数的参数是架构的名称。

以SQL语句作为(executeQuery的)参数,调用Statement::executeQuery函数执行查询语句。executeQuery()返回一个ResultSet对象。读操作。

Statement::executeUpdate函数可用于执行特定的SQL语句,如INSERT,UPDATE,DELETE,或者是不返回任何结果的SQL语句,如SQL DDL语句。写操作。

与excuteQuery()不同,excuteUpdate函数不返回ResultSet对象。相反,它返回INSERT,UPDATE,DELETE操作后受影响的行数

如果你事先并不知道SQL语句是SELECT还是INSERT,UPDATE或DELETE,你可以使用execute函数。

当SQL语句是SELECT操作时,execute()返回true,当SQL语句是INSERT,UPDATE,DELETE操作时,execute()返回false。

如果语句是SELECT查询操作,你可以调用Statement实例中的getResultSet函数获取查询结果集。

如果语句是INSERT,UPDATE,DELETE操作,你可以调用getUpdateCount()获取受影响的行数。

在少数情况下,一条SQL语句可能返回多个结果和/或更新行数。一般情况下,你可以忽略这一点。除非你执行一个存储过程,你知道可能会返回多个结果;或者动态执行未知的SQL语句。使用getResultSet或getUpdateCount函数可以获取结果,getMoreResults()可以检查是否还有其他结果集合。

/* connection.h */
void Connection::setSchema(const std::string& catalog);

/* statement.h */
ResultSet* Statement::executeQuery (const std::string& sql);
int Statement::executeUpdate (const std::string& sql);
bool Statement::execute (const std::string& sql);

ResultSet* Statement::getResultSet();
uint64_t Statement::getUpdateCount();

上述所有函数都可能抛出SQLException(SQL异常),所以在代码中必须确保catch(捕捉)到这些异常

Statement *stmt;
ResultSet *res;

res = stmt -> executeQuery ("SELECT * FROM City");

从结果集中获取数据

存储在ResultSet中的数据可以通过getXXX函数来获取,例如getString() 或 getInt(),根据数据类型的不同使用相应的函数获取数据。使用使用ResulSet的next和previous函数移动游标指向下一行数据。

ResultSet会一直保持打开,即使生成他的Statement对象关闭了,重新执行可以获取多个结果集队列中的下一个结果。一旦Statement生成了结果集,该ResultSet会一直有效,直到显示或隐式的关闭它(才会失效),不论产生它的Statement对象状态如何,都不会影响到已经产生的ResultSet。

下列代码片段遍历ResultSet对象res,通过准确的列名获取每一行数据,并显示在标准输出上。

while (res -> next()) {
    cout << rs -> getString("CityName") << endl;
}

因为也可以通过列的索引访问,所以下列代码片段也能得到相同的效果:

while (res -> next()) {
        cout << rs -> getString(1) << endl;
}

getString()的整形参数引用查询语句中指定的列,列号从1开始。(译者注:比如“SELECT COLUMN1 COLUMN2 COLUMN3 FROM TABLE”返回的结果中,第一列就是COLUMN1,第二列为COLUMN2,第三列为COLUMN3,通过getString(1)、getString(2)、getString(3)获取对应数据)

这两个版本的getString()都返回列值。如果列值为空(NULL),则返回值为空字符串(string对象)。你可以通过ResultSet::isNull函数,以列索引或列名/列标签作为参数,检查相应的列值是否为SQL NULL。通过ResultSet::wasNull()函数可以确定读取的最后一列的值是否为SQL NULL,此函数没有参数。

下列实例演示如何反向遍历结果集中的数据:

/* Move the cursor to the end of the ResultSet object, just after the last row */
res -> afterLast();

if (!res -> isAfterLast()) {
    throw runtime_error("Error: Cursor position should be at the end of the result set after the last row.");
}

/* fetch the data : retrieve all the rows in the result set */
while (res -> previous()) {
    cout << rs -> getString("CityName") << endl;
}

如果列名/列标签无效,或出现数据库访问错误,或在已关闭的ResultSet对象上调用getString(),getString()都会抛出SQLException.

发布了265 篇原创文章 · 获赞 20 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/ccnu027cs/article/details/103805869