Data documentation update

This commit is contained in:
Aleksandar Fabijanic
2012-09-25 23:39:33 +00:00
parent a4e100f286
commit 0c05ec4701
3 changed files with 61 additions and 63 deletions

View File

@@ -259,7 +259,7 @@ Extract now changes to:
virtual void prepare(std::size_t pos, const BLOB&) = 0; virtual void prepare(std::size_t pos, const BLOB&) = 0;
---- ----
Note that it is recommended to prepare a statement only once in the compileImpl of <[StatementImpl]>. The AbstractPrepare objects (which make use of <[AbstractPreparation]> Note that it is recommended to prepare a statement only once in the compileImpl of <[StatementImpl]>. The AbstractPreparator objects (which make use of <[AbstractPreparation]>
can be created by iterating over the Extractor objects of the StatementImpl: can be created by iterating over the Extractor objects of the StatementImpl:
Poco::Data::AbstractExtractingVec::iterator it = extractings().begin(); Poco::Data::AbstractExtractingVec::iterator it = extractings().begin();
@@ -267,7 +267,7 @@ can be created by iterating over the Extractor objects of the StatementImpl:
std::size_t pos = 0; // sqlite starts with pos 0 for results! your DB maybe with 1 std::size_t pos = 0; // sqlite starts with pos 0 for results! your DB maybe with 1
for (; it != itEnd; ++it) for (; it != itEnd; ++it)
{ {
AbstractPrepare* pPrep = (*it)->createPrepareObject(pPreparation, pos); AbstractPreparator* pPrep = (*it)->createPrepareObject(pPreparation, pos);
_prepareVec.push_back(pPrep); _prepareVec.push_back(pPrep);
(*it)->extract(pos); (*it)->extract(pos);
pos += (*it)->numOfColumnsHandled(); pos += (*it)->numOfColumnsHandled();

View File

@@ -1,26 +1,22 @@
POCO Data Release Notes POCO Data Release Notes
Data Data
!!!Release 1.3 !!!Release 1.5.0
Release 1.3 of the POCO C++ Libraries is the first official release containing the Data library. The Data library has been available since the 1.2 release. For the 1.5.0 release, a few things have been changed in an incompatible way that requires changes
The Data library has been available in a development state for the 1.2 release. For the 1.3
release, a few things have been changed in an incompatible way that requires changes
to existing code. to existing code.
!!Summary of Changes !!Summary of Changes
- Class Poco::Data::RecordSet has been added providing generic access - RowFormatter class for convenient output formatting.
to arbitrary tables. - Stored procedures support (for databases that support it).
- SessionInstantiator has been renamed Poco::Data::Connector. - Transaction support (for databases that support it).
- Poco::Data::BLOBInputStream and Poco::Data::BLOBOutputStream allow convenient access to Poco::Data::BLOB data. - Bulk execution (for ODBC drivers that support it).
- Poco::Data::Session and Poco::Data::Statement can be used in simpler ways. - Batch queries and multiple results (for databases and ODBC drivers that support it).
- The DataConnectors project directory has been merged into the Data project directory. - Stored procedures/functions support (for databases that support it)
- Session pool containers.
!!Incompatible Changes and Possible Transition Issues !!Incompatible Changes and Possible Transition Issues
SessionInstantiator has been renamed Poco::Data::Connector, and the Keywords (use, into, limit, etc) now reside in Poco::Data::Keywords namespace.
member functions addToFactory() and removeFromFactory() are now named
registerConnector() and unregisterConnector(), respectively.
This requires a change in all applications already using POCO Data.

View File

@@ -7,11 +7,12 @@ send/retrieve data to/from various different SQL databases.
The following complete example shows how to use it: The following complete example shows how to use it:
#include "Poco/Data/Common.h"
#include "Poco/Data/SQLite/Connector.h" #include "Poco/Data/SQLite/Connector.h"
#include "Poco/Data/Session.h"
#include <iostream> #include <iostream>
using namespace Poco::Data; using namespace Poco::Data;
using namespace Poco::Data::Keywords;
void init() void init()
@@ -77,22 +78,25 @@ Inserting data works by <* using *> the content of other variables. Assume we ha
ForeName (Name VARCHAR(30)) ForeName (Name VARCHAR(30))
---- ----
If we want to insert one single forename we could simply write: If we want to insert one single forename we could write:
std::string aName("Peter"); std::string aName("Peter");
ses << "INSERT INTO FORENAME VALUES(" << aName << ")", now; ses << "INSERT INTO FORENAME VALUES(" << aName << ")", now;
---- ----
Well, we could do that, but we won't. A much better solution is to use <!placeholders!> and connect each placeholder via a <!use!> Another way is to use <!placeholders!> and connect each placeholder via a <!use!>
expression with a variable that will provide the value during execution. expression with a variable that will provide the value during execution.
Placeholders are recognized by having a <!:!> in front of their name. Rewriting the above code now simply gives Universal placeholders are question marks <!?!> . Rewriting the above code now simply gives
std::string aName("Peter"); std::string aName("Peter");
ses << "INSERT INTO FORENAME VALUES(:name)", use(aName), now; ses << "INSERT INTO FORENAME VALUES(?)", use(aName), now;
---- ----
In this example the <!use!> expression matches the <* :name *> with the <*Peter*> value. In this example the <!use!> expression matches the <* ? *> with the <*Peter*> value.
Note that apart from the nicer syntax, the real benefit of placeholders - which is performance - doesn't show here. Note that apart from the nicer syntax, the real benefit of placeholders - which is performance - doesn't show here.
Some database systems (e.g. SQLite) support descriptive placeholders (e.g. !:name!) but, for universal appliciablity,
it is recommended to use the questin mark.
Check the <*Working with Statements*> section to find out more. Check the <*Working with Statements*> section to find out more.
Retrieving data from the Database works similar. The <!into!> expression matches the returned database values to Retrieving data from the Database works similar. The <!into!> expression matches the returned database values to
@@ -117,7 +121,7 @@ Lets assume we have a Person table that contains an age, a first and a last name
std::string firstName("Peter"; std::string firstName("Peter";
std::string lastName("Junior"); std::string lastName("Junior");
int age = 0; int age = 0;
ses << INSERT INTO PERSON VALUES (:fn, :ln, :age)", use(firstName), use(lastName), use(age), now; ses << INSERT INTO PERSON VALUES (?, ?, ?)", use(firstName), use(lastName), use(age), now;
ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age), now; ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age), now;
---- ----
@@ -127,25 +131,19 @@ The same is true for the <*into*> statement. We select <*firstname*> as the firs
thus <*into(firstName)*> must be the first into clause. thus <*into(firstName)*> must be the first into clause.
!! Handling NULL entries !! Handling NULL entries
A common case with databases are optional data fields that can contain NULL. To accomodate for NULL, the <*into*> expression allows A common case with databases are optional data fields that can contain NULL. To accomodate for NULL, use Nullable template:
you to define default values.
For example, assume that age is such an optional field and we want to provide as default value <!-1!>
which is done by writing <!into(age, -1)!>:
std::string firstName("Peter"; std::string firstName("Peter";
std::string lastName("Junior"); std::string lastName("Junior");
int age = 0; Nullable<int> age = 0;
ses << INSERT INTO PERSON VALUES (:fn, :ln, :age)", use(firstName), use(lastName), use(age), now; ses << INSERT INTO PERSON VALUES (?, ?, ?)", use(firstName), use(lastName), use(age), now;
ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age, -1), now; ses << "SELECT (firstname, lastname, age) FROM Person", into(firstName), into(lastName), into(age), now;
---- ----
While you can achieve the same effect by initializing age previously to -1 (<*int age = -1*>), Nullable is a template wrapping any type with purpose of allowing it to have null value.
this won't work with collection types. Here you must provide the second parameter
to init. Otherwise, values will be initialized to compiler specific values.
!!!Working with Statements !!!Working with Statements
We often mentioned the term <*Statement*> in the previous section, yet we only worked with database session objects so far, We mentioned the term <*Statement*> in the previous section, yet we only worked with database session objects so far.
or at least, that's what you have been made believe ;-).
In reality, you have already worked with Statements. Lets take a look at the method signature of the << operator at Session: In reality, you have already worked with Statements. Lets take a look at the method signature of the << operator at Session:
template <typename T> template <typename T>
@@ -159,19 +157,19 @@ part which executed the statement. Afterwards the statement was destroyed.
Let's take one of the previous examples and change it so that we assign the statement: Let's take one of the previous examples and change it so that we assign the statement:
std::string aName("Peter"); std::string aName("Peter");
Statement stmt = ( ses << "INSERT INTO FORENAME VALUES(:name)", use(aName) ); Statement stmt = ( ses << "INSERT INTO FORENAME VALUES(?)", use(aName) );
---- ----
Note that we must put brackets around the right part of the assignment, otherwise the compiler will complain. Note that the parenthesis around the right part of the assignment are necessary to compile.
If you don't like the above syntax, the following alternative is equivalent: Here is an equivalent syntax without parenthesis:
Statement stmt(ses); Statement stmt(ses);
stmt << "INSERT INTO FORENAME VALUES(:name)", use(aName); stmt << "INSERT INTO FORENAME VALUES(?)", use(aName);
---- ----
What did we achieve by assigning the statement to a variable? Well, currently nothing, apart that we can control when to <*execute*>: What did we achieve by assigning the statement to a variable? Well, currently nothing, apart that we can control when to <*execute*>:
std::string aName("Peter"); std::string aName("Peter");
Statement stmt = ( ses << "INSERT INTO FORENAME VALUES(:name)", use(aName) ); Statement stmt = ( ses << "INSERT INTO FORENAME VALUES(?)", use(aName) );
stmt.execute(); stmt.execute();
poco_assert (stmt.done()); poco_assert (stmt.done());
---- ----
@@ -188,7 +186,7 @@ A prepared statement is created by omitting the <*now*> clause.
The advantage of a prepared statement is performance. Assume the following loop: The advantage of a prepared statement is performance. Assume the following loop:
std::string aName(); std::string aName();
Statement stmt = ( ses << "INSERT INTO FORENAME VALUES(:name)", use(aName) ); Statement stmt = ( ses << "INSERT INTO FORENAME VALUES(?)", use(aName) );
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
aName.append("x"); aName.append("x");
@@ -202,15 +200,19 @@ Still, this isn't the best way to insert a collection of values into a database.
!!Things NOT To Do !!Things NOT To Do
<!use!> expects as input a <!reference!> parameter, which is bound later during execution. <!use!> expects as input a <!reference!> parameter, which is bound later during execution.
Thus, one can only use variables, but never constants. To prevent binding to a non-existing storage, the following will not compile:
The following code will very likely fail (but this is platform/compiler dependent and also depends if your
building in release or debug mode, it will work from Monday to Thursday but will always fail on Friday, so shortly spoken: the kind of bugs
software developers <*really*> love):
Statement stmt = (ses << INSERT INTO PERSON VALUES (:fn, :ln, :age)", use("Peter"), use("Junior"), use(4)); //ERR! Statement stmt = (ses << "INSERT INTO PERSON VALUES (?, ?, ?)", use("Peter"), use("Junior"), use(4)); //ERR!
stmt.execute();
----
The constant values <*Junior*>, <*Peter*> and <*4*> must be assigned to variables priorto execution:
std::string fn("Peter"), ln("Junior");
int age = 4;
Statement stmt = (ses << "INSERT INTO PERSON VALUES (?, ?, ?)", use(fn), use(ln), use(age)); //ERR!
stmt.execute(); stmt.execute();
---- ----
The constant values <*Junior*>, <*Peter*> and <*4*> must be assigned to variables prior, otherwise their values will be invalid when execute is called.
!!!Collection Support !!!Collection Support
If one needs to handle many values at once, one ought to use a collection class. If one needs to handle many values at once, one ought to use a collection class.
@@ -230,7 +232,7 @@ A bulk insert example via vector would be:
aName.append("x"); aName.append("x");
data.push_back(aName); data.push_back(aName);
} }
ses << "INSERT INTO FORENAME VALUES(:name)", use(data), now; ses << "INSERT INTO FORENAME VALUES(?)", use(data), now;
---- ----
The same example would work with set or multiset but not with map and multimap (std::string has no () operator). The same example would work with set or multiset but not with map and multimap (std::string has no () operator).
@@ -322,33 +324,33 @@ The third parameter to range is an optional boolean value which specifies if the
if the amount of rows returned by the query must match exactly. Per default exact matching is off. if the amount of rows returned by the query must match exactly. Per default exact matching is off.
!!!Complex Data Type Mapping !!!Complex Data Type Mapping
All the previous examples were contented to work with only the most basic data types: integer, string, ... All the previous examples were intented to work with only the most basic data types: integer, string, ...
a situation, unlikely to occur in real-world scenarios. a situation, unlikely to occur in real-world scenarios.
Assume you have a class Person: Assume you have a class Person:
class Person class Person
{ {
public: public:
// default constructor+destr. // default ctor & dtor.
// getter and setter methods for all members // getter and setter methods for all members
[...] [...]
bool operator <(const Person& p) const bool operator <(const Person& p) const
/// we need this for set and multiset support /// we need this for set and multiset support
{ {
return _socialSecNr < p._socialSecNr; return _ssn < p._ssn;
} }
Poco::UInt64 operator()() const Poco::UInt64 operator()() const
/// we need this operator to return the key for the map and multimap /// we need this operator to return the key for the map and multimap
{ {
return _socialSecNr; return _ssn;
} }
private: private:
std::string _firstName; std::string _firstName;
std::string _lastName; std::string _lastName;
Poco::UInt64 _socialSecNr; Poco::UInt64 _ssn;
} }
---- ----
@@ -371,21 +373,21 @@ The template specialization must implement the following methods:
static void bind(std::size_t pos, const Person& obj, AbstractBinder* pBinder) static void bind(std::size_t pos, const Person& obj, AbstractBinder* pBinder)
{ {
poco_assert_dbg (pBinder != 0); poco_assert_dbg (pBinder != 0);
// the table is defined as Person (FirstName VARCHAR(30), lastName VARCHAR, SocialSecNr INTEGER(3)) // the table is defined as Person (FirstName VARCHAR(30), lastName VARCHAR, SSN INTEGER(3))
// Note that we advance pos by the number of columns the datatype uses! For string/int this is one. // Note that we advance pos by the number of columns the datatype uses! For string/int this is one.
TypeHandler<std::string>::bind(pos++, obj.getFirstName(), pBinder); TypeHandler<std::string>::bind(pos++, obj.getFirstName(), pBinder);
TypeHandler<std::string>::bind(pos++, obj.getLastName(), pBinder); TypeHandler<std::string>::bind(pos++, obj.getLastName(), pBinder);
TypeHandler<Poco::UInt64>::bind(pos++, obj.getSocialSecNr(), pBinder); TypeHandler<Poco::UInt64>::bind(pos++, obj.getSSN(), pBinder);
} }
static void prepare(std::size_t pos, const Person& obj, AbstractPreparation* pPrepare) static void prepare(std::size_t pos, const Person& obj, AbstractPreparation* pPrepare)
{ {
poco_assert_dbg (pBinder != 0); poco_assert_dbg (pBinder != 0);
// the table is defined as Person (FirstName VARCHAR(30), lastName VARCHAR, SocialSecNr INTEGER(3)) // the table is defined as Person (FirstName VARCHAR(30), lastName VARCHAR, SSN INTEGER(3))
// Note that we advance pos by the number of columns the datatype uses! For string/int this is one. // Note that we advance pos by the number of columns the datatype uses! For string/int this is one.
TypeHandler<std::string>::prepare(pos++, obj.getFirstName(), pPrepare); TypeHandler<std::string>::prepare(pos++, obj.getFirstName(), pPrepare);
TypeHandler<std::string>::prepare(pos++, obj.getLastName(), pPrepare); TypeHandler<std::string>::prepare(pos++, obj.getLastName(), pPrepare);
TypeHandler<Poco::UInt64>::prepare(pos++, obj.getSocialSecNr(), pPrepare); TypeHandler<Poco::UInt64>::prepare(pos++, obj.getSSN(), pPrepare);
} }
static void extract(std::size_t pos, Person& obj, const Person& defVal, AbstractExtractor* pExt) static void extract(std::size_t pos, Person& obj, const Person& defVal, AbstractExtractor* pExt)
@@ -394,13 +396,13 @@ The template specialization must implement the following methods:
poco_assert_dbg (pExt != 0); poco_assert_dbg (pExt != 0);
std::string firstName; std::string firstName;
std::string lastName; std::string lastName;
Poco::UInt64 socialSecNr = 0; Poco::UInt64 ssn = 0;
TypeHandler<std::string>::extract(pos++, firstName, defVal.getFirstName(), pExt); TypeHandler<std::string>::extract(pos++, firstName, defVal.getFirstName(), pExt);
TypeHandler<std::string>::extract(pos++, lastName, defVal.getLastName(), pExt); TypeHandler<std::string>::extract(pos++, lastName, defVal.getLastName(), pExt);
TypeHandler<Poco::UInt64>::extract(pos++, socialSecNr, defVal.getSocialSecNr(), pExt); TypeHandler<Poco::UInt64>::extract(pos++, ssn, defVal.getSSN(), pExt);
obj.setFirstName(firstName); obj.setFirstName(firstName);
obj.setLastName(lastName); obj.setLastName(lastName);
obj.setSocialSecNr(socialSecNr); obj.setSSN(ssn);
} }
}; };
@@ -485,7 +487,7 @@ Consider the following example:
people.push_back(Person("Lisa Simpson", "Springfield", 10)); people.push_back(Person("Lisa Simpson", "Springfield", 10));
Statement insert(session); Statement insert(session);
insert << "INSERT INTO Person VALUES(:name, :address, :age)", insert << "INSERT INTO Person VALUES(?, ?, ?)",
use(people), now; use(people), now;
---- ----
@@ -500,7 +502,7 @@ Of course, tuples can also be used in queries:
{ {
std::cout << "Name: " << it->get<0>() << std::cout << "Name: " << it->get<0>() <<
", Address: " << it->get<1>() << ", Address: " << it->get<1>() <<
", Age: " << it->get<2>() <<std::endl; ", Age: " << it->get<2>() << std::endl;
} }
---- ----