Вы находитесь на странице: 1из 13

4/9/2012

MySQL & Connector/C++ Guide


r3dux.org

Table of Contents
As we covered an introduction to MySQL last week, we'll be moving on to how we can connect to a MySQL database with C++ this week, and will move on to how we can guarantee transactional integrity in tomorrow's class. Topics that we'll cover today include:

Connecting to MySQL with Connector/C++ Exercises (2) Create your own project which connects to a database Create a database and a table, then store a limerick in the database via Connector/C++

30/11/2010

4/9/2012

Ways of Connecting to MySQL Databases


There are many ways of connecting to MySQL databases, mostly these involve using some variety of the MySQL Connector library. Since we're using C++ there are two ways that we can connect to a MySQL database:

Via Connector/C (aka libmysql), or Via Connector/C++

Although the standard C connector will work, it presents a number of issues for us. As it's based on C (and not C++) it means we can't use strings, we can't use objects to pass data to/from the database, and generally we're hamstringing ourselves. In fact, the only positive is that connecting to a database through libmysql is that it's very simple.

30/11/2010

Ways of Connecting to MySQL Databases (Cont'd)


We're going to take the best-practice approach and use Connector/C++ http://dev.mysql.com/downloads/connector/ For all intents and purposes this means that:

We get to use strings and objects, We're using a modern object-oriented API.

Unfortunately for us there are a few minor drawbacks to this:

To use Connector/C++ with our projects, we also need to include the boost library (which is basically an extension to the STL)... ...which means our project folders will be approx. 62MB per project. And the code we use will be a little bit verbose (but not as bad as jumping through all the hoops we'd need to pass data between C and C++ objects)

30/11/2010

4/9/2012

Database Architecture & Prerequisites


MySQL (or any modern DBMS, for that matter) will always come in two "halves":

A Server component which stores the data and handles input and output requests, and A Client component which connects to the database and provides the input commands (i.e. INSERT/UPDATE) or requests data from (i.e. SELECT) the server. This is the part that we'll be writing.

It won't come as any surprise that both halves need to be up and running for any meaningful communication to take place! So the first thing you need to do is to start your WAMP server via: Start | All Programs | wamp server | Start wamp server

30/11/2010

Connecting to a MySQL Database


To start off our MySQL client, we need the following includes:
// Standard C++ includes #include <iostream> #include <sstream> // Needed to convert things to strings #include <cstdlib> #include <string> using namespace std;

// Connector/C++ headers. Must be placed in our project folder. #include "cppconn/driver.h" #include "cppconn/exception.h" #include "cppconn/resultset.h" #include "cppconn/statement.h"

// Link to the Connector/C++ library - must be in folder #pragma comment(lib, "mysqlcppconn.lib")


6 30/11/2010

4/9/2012

Connecting to a MySQL Database (Cont'd)


To make a connection to the database server itself, we must have a set of valid details/credentials which includes:

The IP address or hostname of the database server we want to connect to, along with the port number we should connect on, A valid database server username, and A valid password for this username.

Setting up users/passwords and their permissions is outside the scope of this course - just be aware that WAMP server comes with a MySQL administration account called root which has no password.
// Specify our connection target and credentials const string server = "tcp://127.0.0.1:3306";

const string username = "root"; const string password = ""; // No password - very poor!

30/11/2010

Connecting to a MySQL Database (Cont'd)


Once we have a valid connection target and some credentials to use there are couple of steps that we need to take in our code to connect to the database server and do something useful once we're connected. In this specific order, we need to:

Get a database connection driver, Connect to the database, Perform some transactions as required, and finally Disconnect from the database

We'll look at how we go about those step by step in the following slides.

30/11/2010

4/9/2012

Connecting to a MySQL Database (Cont'd)


For us to connect to the database server, we're going to use some of the functionality provided by the Connector/C++ library, namely we'll need to: Create a database Driver object: sql::Driver *driver; Create a database Connection object: sql::Connection *dbConn; Create a SQL Statement object to hold our SQL command: sql::Statement *stmt;

And finally, create a SQL ResultSet object to hold the results returned from the database: sql::ResultSet *res;

30/11/2010

Getting a Valid Driver


To connect to the MySQL database server we need a valid driver to manage our connection. This can be accomplished through the following code: // Try to get a driver to use to connect to our DBMS try { driver = get_driver_instance(); } catch (sql::SQLException e) { cout << "Couldn't get database driver: " << e.what() << endl; system("pause"); exit(1); }

10

30/11/2010

4/9/2012

A Quick Pointer Reminder


When we have a pointer to something, all we have in the pointer is the memory address of the object or function or whatever, the pointer doesn't store the data itself. To perform an operation on a pointer we must dereference it so we can get the data stored AT the location, and not the value of the location itself! If we wanted to call a function on an object, we use the dot notation i.e. someObject.doSomething(); - but we can't do that with pointers because they only store a memory address! To get around this, in C++ we use the -> notation (dash followed by greater than) to specify that we mean the data the pointer is pointing at! And by using this notation we can run a function on the data being pointed at by a pointer. i.e. Spaceship *pMySpaceship; pMySpaceship->fireAllGuns();

11

30/11/2010

Making the Connection to the Database Server


Once we have a driver, we can attempt to connect to the server. // Try to connect to the DBMS server try { dbConn = driver->connect(server, username, password); } catch (sql::SQLException e) { cout << "Couldn't connect to database: " << e.what() << endl; system("pause"); exit(1); }

12

30/11/2010

4/9/2012

Specifying which Connection our Statement should use


In a MySQL client program, you may be working with more than one database connection - we could, for example, have dozens of connections to dozens of different databases (think of a flight prices website - how many connections do they use? Lots!) We only have one database server to query, so this isn't an issue for us, but we still need to specify which database our query will be running on! To do this, we need to bind our Statement object to the Connection object we created earlier, which we can do like this: stmt = dbConn->createStatement();

13

30/11/2010

Querying the Database


Now we have everything in place to talk to the database, so let's! // Try to query the database try { stmt->execute("USE mysql"); // Select which database to use res = stmt->executeQuery("show tables"); // Perform a query } catch (sql::SQLException e) { system("pause"); exit(1); } Notice that we use the executeQuery command! Notice that we use the execute command!

cout << "SQL error. Error message: " << e.what() << endl;

14

30/11/2010

4/9/2012

Displaying the Results of our Query - The Bad Way!


Once the MySQL database has passed us back the results of our query (into a ResultSet object, which contains all the results), we can now go through it line by line (i.e. record by record in the result set). There are two ways we can go about this:

By field number (the bad way), and By field name (the good way!).

// While there are still results (i.e. rows) in our result set... while (res->next()) { // ...get each field we want and output it to the screen // Note: The first field/column in our result-set is // field 1 (one) and -NOT- field 0 (zero) cout << res->getString(1) << endl; }
15 30/11/2010

Displaying the Results of our Query - The Good Way!


A better way to get the data by field from our result set is to use the field names (of course, this depends on us knowing the field names!) Suppose I had a database with the fieldnames playerName and score (which was an integer), then I could use: // While there are still results (i.e. rows) in our result set... while (res->next()) { // ...get each field we want and output it to the screen cout << res->getString("playerName") << endl; cout << res->getInt("score") << endl; }

16

30/11/2010

4/9/2012

A Quick Note on Apostrophes


Consider a command to insert a string followed by an integer into a database using SQL: INSERT INTO students (name, age) VALUES ('Jon Lewis', 32); The string part of the statement (i.e. Jon Lewis) needs to be enclosed in apostrophes to indicate that it's a string, while the integer part (i.e. 32) does NOT get enclosed in apostrophes to indicate that it's of numerical type. When we're creating our own SQL strings (i.e. a C++ string which contains an entire SQL statement) we have to follow the exact same convention! This can make things a bit fiddly and there's a different method called prepared statements that we can use which we'll discuss tomorrow - but if you take your time with it we shouldn't have too many problems!

17

30/11/2010

Inserting Data into a Database


Inserting data into a database can be a bit fiddly because of the limitation of string concatenation, so we're going to use a helper function which takes any data type and allows us to append it to a string. It's not so important to know how it works right now - just that it does! // A template function to allow us to convert things to strings // Note: This function requires that we include <sstream> template<class T> std::string toString(const T& t) { std::ostringstream stream; stream << t; return stream.str(); }
18 30/11/2010

4/9/2012

Inserting Data into a Database (Cont'd)


If I had a database called highscores which had a table in it called level1 which contained the fields playerName and playerScore, then I could get data from the user and insert it into the database like this: // Get data from the user and insert it into the database try { stmt->execute("USE highscores"); // Select our database // Variables to hold our input data string name; int score; // Continued on next slide...

19

30/11/2010

Inserting Data into a Database (Cont'd)


// Get user input cout << "Please enter your name: "; cin >> name; cout << "Please enter your score: "; cin >> score; // Construct our SQL statement as a string string sqlStr = "INSERT INTO level1 (playerName, playerScore) VALUES ('"; // Notice the opening apostrophe! sqlStr += name; sqlStr += "',"; // Notice the closing apostrophe! sqlStr += toString(score); // Convert our int to a string and append it. No apostrophes because it's a numerical type!! sqlStr += ")";

20

30/11/2010

10

4/9/2012

Inserting Data into a Database (Cont'd)


Once we have our properly constructed string containing our SQL command we can execute it on the database like this: /* Use executeUpdate to insert the data into the DB. The difference between "execute" and "executeUpdate" is that the latter passes back the number of rows affected by our SQL command! */ int affectedRows = stmt->executeUpdate(sqlStr); cout << "Done! Num. rows affected: " << affectedRows; The number of rows affected can be useful for when we're updating records and we want to know how many rows we updated.

21

30/11/2010

Inserting Data into a Database (Cont'd)


Because we've been doing the last couple of slides of code within a try block, we need to place a relevant catch block after it in case any of our SQL commands failed (bad syntax, wrong data-types, typos etc.) res = stmt->executeQuery(sqlStr); } catch (sql::SQLException e) { cout << "SQL error. Error message: " << e.what() << endl; system("pause"); exit(1); }

22

30/11/2010

11

4/9/2012

Cleaning Up After Ourselves


Once we have done everything we wanted to do with our database connection it's good form to get rid of our database-related objects. // Clean up after ourselves delete res; delete stmt; delete dbConn;

That's it - we're done!

23

30/11/2010

Exercises
1) Create a Win32 console application that will connect to a MySQL database and display all fields from the USER_PRIVILEGES table in the information_schema database. 2) Create a second Win32 console application that will connect to a MySQL database. Include the facility for the user to enter five strings to form a limerick. Note: Use code in the following form to get a line of text from the user: string firstLine; cout << "Please enter the first line of the limerick: "; getline(cin, firstLine)

1) Modify the SQL in your second project so that it: Checks if a database called Limericks exists. If it doesn't then the database must be created. Checks if a table called UserLimericks exists. If it doesn't then the table must be created.The format of the table is just five VARCHARs
24

Inserts the user's limerick into the database, and then 30/11/2010

12

4/9/2012

Hints
The SQL command to check if a database exists and create it if not is: CREATE DATABASE IF NOT EXISTS name-of-database; The SQL command to check if a table in a database exists and create it if not is: CREATE TABLE IF NOT EXISTS name-of-table; Remember that you have to be USEing a database before you can do anything to it with SQL! The table structure for the limericks table can just be five VARCHARs called line1, line2, line3 etc.

25

30/11/2010

Example Limericks
A programming genius called Hank Wrote a system to access his bank When his memory failed him They nailed him then jailed him Now his storage is basic and dank.

A programming genius called HEAP Had trouble in getting to sleep So he made his lambs troup through a huge FOR-NEXT loop FOR 1 TO 10000: NEXT sheep.

26

30/11/2010

13

Вам также может понравиться