Added a simple RAII Transaction class

This commit is contained in:
Sébastien Rombauts 2012-04-04 19:15:23 +02:00
parent acf27fd052
commit c4f041cb46
4 changed files with 183 additions and 4 deletions

View File

@ -11,3 +11,4 @@
#include "Database.h" #include "Database.h"
#include "Statement.h" #include "Statement.h"
#include "Transaction.h"

View File

@ -0,0 +1,49 @@
/**
* @file Transaction.cpp
* @brief A prepared SQLite Transaction is a compiled SQL query ready to be executed.
*
* Copyright (c) 2012 Sebastien Rombauts (sebastien dot rombauts at gmail dot com)
*
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
* or copy at http://opensource.org/licenses/MIT)
*/
#include "Transaction.h"
#include "Database.h"
namespace SQLite
{
// Compile and register the SQL query for the provided SQLite Database Connection
Transaction::Transaction(Database &aDatabase) : // throw(SQLite::Exception)
mDatabase(aDatabase),
mbCommited(false)
{
mDatabase.exec("BEGIN");
}
// Finalize and unregister the SQL query from the SQLite Database Connection.
Transaction::~Transaction(void) throw() // nothrow
{
if (false == mbCommited)
{
mDatabase.exec("ROLLBACK");
}
}
// Commit the transaction.
void Transaction::commit(void) // throw(SQLite::Exception)
{
if (false == mbCommited)
{
mDatabase.exec("COMMIT");
mbCommited = true;
}
else
{
throw SQLite::Exception("Transaction already commited");
}
}
}; // namespace SQLite

View File

@ -0,0 +1,64 @@
/**
* @file Transaction.h
* @brief A Transaction is way to group multiple SQL statements into an atomic secured operation.
*
* Copyright (c) 2012 Sebastien Rombauts (sebastien dot rombauts at gmail dot com)
*
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
* or copy at http://opensource.org/licenses/MIT)
*/
#pragma once
#include <sqlite3.h>
#include "Exception.h"
namespace SQLite
{
// Forward declaration
class Database;
/**
* @brief RAII encapsulation of a SQLite Transaction.
*
* A Transaction is a way to group multiple SQL statements into an atomic secured operation;
* either it succeeds, with all the changes commited to the database file,
* or if it fails, all the changes are rolled back to the initial state.
*
* Resource Acquisition Is Initialization (RAII) means that the Transaction
* begins in the constructor and is rollbacked in the destructor, so that there is
* no need to worry about memory management or the validity of the underlying SQLite Connection.
*/
class Transaction
{
public:
/**
* @brief Begins the SQLite transaction
*
* Exception is thrown in case of error, then the Transaction is NOT initiated.
*/
explicit Transaction(Database &aDatabase); // throw(SQLite::Exception);
/**
* @brief Safely rollback the transaction if it has not been commited.
*/
virtual ~Transaction(void) throw(); // nothrow
/**
* @brief Commit the transaction.
*/
void commit(void); // throw(SQLite::Exception);
private:
// Transaction must not be copyable
Transaction(void);
Transaction(const Transaction&);
Transaction& operator=(const Transaction&);
private:
Database& mDatabase; //!< Reference to the SQLite Database Connection
bool mbCommited; //!< True when the last executeStep() had no more row to fetch
};
}; // namespace SQLite

View File

@ -15,6 +15,7 @@
#include "../SQLiteC++/Database.h" #include "../SQLiteC++/Database.h"
#include "../SQLiteC++/Statement.h" #include "../SQLiteC++/Statement.h"
#include "../SQLiteC++/Transaction.h"
/// Object Oriented Basic example /// Object Oriented Basic example
@ -56,7 +57,7 @@ private:
int main (void) int main (void)
{ {
// Basic example (1/3) : // Basic example (1/4) :
try try
{ {
// Open a database file // Open a database file
@ -64,7 +65,7 @@ int main (void)
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n"; std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
// Compile a SQL query, containing one parameter (index 1) // Compile a SQL query, containing one parameter (index 1)
SQLite::Statement query(db, "SELECT * FROM test WHERE size > ?;SELECT id FROM test"); SQLite::Statement query(db, "SELECT * FROM test WHERE size > ?");
std::cout << "SQLite statement '" << query.getQuery().c_str() << "' compiled (" << query.getColumnCount () << " columns in the result)\n"; std::cout << "SQLite statement '" << query.getQuery().c_str() << "' compiled (" << query.getColumnCount () << " columns in the result)\n";
// Bind the integer value 6 to the first parameter of the SQL query // Bind the integer value 6 to the first parameter of the SQL query
query.bind(1, 6); query.bind(1, 6);
@ -99,7 +100,7 @@ int main (void)
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Object Oriented Basic example (2/3) : // Object Oriented Basic example (2/4) :
try try
{ {
// Open the database and compile the query // Open the database and compile the query
@ -116,7 +117,7 @@ int main (void)
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Simple batch queries example (3/3) : // Simple batch queries example (3/4) :
try try
{ {
// Open a database file // Open a database file
@ -138,5 +139,69 @@ int main (void)
} }
remove("test.db3"); remove("test.db3");
////////////////////////////////////////////////////////////////////////////
// RAII transaction example (4/4) :
try
{
// Open a database file
SQLite::Database db("transaction.db3", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n";
db.exec("DROP TABLE IF EXISTS test");
// Exemple of a successful transaction :
try
{
// Begin transaction
SQLite::Transaction transaction(db);
db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)");
int nb = db.exec("INSERT INTO test VALUES (NULL, \"test\")");
std::cout << "INSERT INTO test VALUES (NULL, \"test\")\", returned " << nb << std::endl;
// Commit transaction
transaction.commit();
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
}
// Exemple of a rollbacked transaction :
try
{
// Begin transaction
SQLite::Transaction transaction(db);
int nb = db.exec("INSERT INTO test VALUES (NULL, \"second\")");
std::cout << "INSERT INTO test VALUES (NULL, \"second\")\", returned " << nb << std::endl;
nb = db.exec("INSERT INTO test \"error\"");
std::cout << "INSERT INTO test \"error\"\", returned " << nb << std::endl;
// Commit transaction
transaction.commit();
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
}
// Check the results
SQLite::Statement query(db, "SELECT * FROM test");
std::cout << "SELECT * FROM test :\n";
while (query.executeStep())
{
std::cout << "row : (" << query.getColumn(0) << ", " << query.getColumn(1) << ")\n";
}
}
catch (std::exception& e)
{
std::cout << "SQLite exception: " << e.what() << std::endl;
}
remove("transaction.db3");
return 0; return 0;
} }