From 884d14f1ea07d5d827554c3a1989e10ba033bafa Mon Sep 17 00:00:00 2001
From: Boris Kolpackov To be able to save person objects in the database we had to make
five changes, marked with (1) to (5), to the orignal class
definition. The first change is the inclusion of the ODB
- headers Let's examine this code piece by piece. At the beginnig we include
- a bunch of headers. Those include 2 Hello World Example
@@ -290,7 +341,7 @@ private:
core.hxx
. This headers provides a number
+ headers <odb/core.hxx>
. This headers provides a number
of core ODB declarations, such as odb::access
, that
are used to define peristent classes.odb/database.hxx
and
- odb/transaction.hxx
which define database
+ a bunch of headers. Those include <odb/database.hxx>
+ and <odb/transaction.hxx>
which define database
system-independant odb::database
and
odb::transaction
interfaces. Then we include
- odb/mysql/database.hxx
which defines the
+ <odb/mysql/database.hxx>
which defines the
MySQL implementation of the database
interface. Finaly,
we include person.hxx
and person-odb.hxx
which define our persistent person
class.
@@
@@ -1256,7 +1310,7 @@ Hello, Joe!database::begin_transaction()
function. The returned transaction handle is stored in
an instance of the odb::transaction
class which is
- defined in the odb/transaction.hxx
header file.
+ defined in the <odb/transaction.hxx>
header file.
A source code fragment that uses ODB transactions should include
this header file. The odb::transaction
class has
the following interface:
@@ -1639,6 +1693,10 @@ db->erase<person> (jane_id);
t.commit ();
+
+
+
+
If you don't know the identifiers of the objects that you are looking @@ -2136,6 +2194,950 @@ namespace odb } + + + + +
As we have already seen in previous chapters, ODB uses a pragma-based + language to capture database-specific information about C++ types. + This chapter describes the ODB pragma language in more detail. It + can be read together with other chapters in the manual to get a + sense of what kind configurations and mapping fine-tuning are + possible. You can also use this chapter as a reference at a later + stage.
+ +An ODB pragma has the following syntax:
+ +#pragma db qualifier [specifier specifier ...]
The qualifier tell the ODB compiler what kind of C++ construct
+ this pragma describes. Valid qualifiers are object
,
+ value
, and member
. Pragmas with the
+ object
qualifier describes persistent object types.
+ It tells the ODB compiler that a C++ class it describes is a
+ persistent class. Similarly, pragmas with the value
+ qualifier describes value types and the member
+ qualfier is used to describe data members of persistent object
+ and value types.
The specifier informs the ODB compiler about a particular property
+ of the C++ declaration. For example, the id
member
+ specifier tell the ODB compiler that this member contains this
+ object's identifier. Below is the declaration of the person
+ class that shows how we can use these qualifiers and specifiers:
+#pragma db object +class person +{ + ... +private: + #pragma db member id + unsigned long id_; + ... +}; ++ +
In the above example we don't explicitly specify which C++ class or
+ data member the pragma belongs to. Rather, the pragma applies to
+ a C++ declaration that immediately follows the pragma. Such pragmas
+ are called positioned pragmas. In positioned pragmas that
+ apply to data members the member
qualifier can be
+ omitted for brievety, for example:
+ #pragma db id + unsigned long id_; ++ +
Note also that if the C++ declaration immediately following a + position pragma is incompatible with the pragma qualifier, an + error will be issued. For example:
+ ++ #pragma db object // Error: expected class instead of data member. + unsigned long id_; ++ + +
While keeping the C++ declarations and database declarations close + together eases maintenance and increases readability, you can also + separate them in different parts of the same header file or even + factor them to a seperate file. To achive this we use the so called + named pragmas. Unlike positioned pragmas, named pragmas + explicitly specify the C++ declaration to which they apply by + adding the declaration name after the pragma qualifier. For example:
+ ++class person +{ + ... +private: + unsigned long id_; + ... +}; + +#pragma db object(person) +#pragma db member(person::id_) id ++ +
Note that in the named pragmas for data members the member
+ qualifier is no longer optional. The C++ declaration name in the
+ named pragmas is resolved using the standard C++ name resolution
+ rules, for example:
+namespace db +{ + class person + { + ... + private: + unsigned long id_; + ... + }; +} + +namespace db +{ + #pragma db object(person) // Resolves db::person. +} + +#pragma db member(db::person::id_) id ++ +
The following code fragment shows how to use the named value + type pragma to map a C++ type to a native database type:
+ ++#pragma db value(bool) type("INT NOT NULL") + +#pragma db object +class person +{ + ... +private: + bool married_; // Mapped to INT NOT NULL database type. + ... +}; ++ +
The C++ header file that defines your persistent classes and + normally contains one or more ODB pragmas is compiled by both + the ODB compiler to generate the database support code and + the C++ compiler to build your application. Some C++ compilers + issue warnings about pragmas that they do not recognize. There + are several ways to deal with this problem. The easiest is to + disable such warnings using one of the compiler-specific command + line options or warning control pragmas. This method is described + in the following sub-section for most popular C++ compiler.
+ +There are also several C++ compiler-independant methods that you
+ can employ. The first is to use the PRAGMA_DB
macro,
+ defined in <odb/core.hxx>
, instead of the
+ #pragma db
directly. This macro expands to the
+ ODB pragma when compiled with the ODB compiler and to an empty
+ string when compiler with other compilers. The following example
+ shows how we can use this macro:
+#include <odb/core.hxx> + +PRAGMA_DB(object) +class person +{ + ... +private: + PRAGMA_DB(id) + unsigned long id_; + ... +}; ++ +
The alternative to using the PRAGMA_DB
macro is to
+ group the #pragma db
directives in blocks that are
+ conditionally included into compilation only when compiled with the
+ ODB compiler. For example:
+class person +{ + ... +private: + unsigned long id_; + ... +}; + +#ifdef ODB_COMPILER +# pragma db object(person) +# pragma db member(person::id_) id +#endif ++ +
The disadvantage of this approach is that it becomes overly verbose + when positioned pragmas are used.
+ +GNU g++ does not issue warnings about unknown pragmas
+ unless requested with the -Wall
command line option.
+ To disable only the unknown pragma warning you can add the
+ -Wno-unknown-pragmas
option after -Wall
,
+ for example:
+g++ -Wall -Wno-unknown-pragmas ... ++ +
Microsoft Visual C++ issues an unknown pragma warning (C4068) at + warning level 1 or higher. This means that unless you have disabled + warnings altogether (level 0), you will see this warning.
+ +To disable this warning via the compiler command line, you can add
+ the /wd4068
C++ compiler option in Visual Studio 2008
+ and earlier. In Visual Studio 2010 there is now a special GUI field
+ where you can enter warning numbers that should be disabled. Simply
+ enter 4068 into this field.
You can also disable this warning only for a specific header or + a fragment of a header using the warning control pragma. For + example:
+ ++#include <odb/core.hxx> + +#pragma warning (push) +#pragma warning (disable:4068) + +#pragma db object +class person +{ + ... +private: + #pragma db id + unsigned long id_; + ... +}; + +#pragma warning (pop) ++ +
The Sun C++ compiler does not issue warnings about unknown pragmas. + As a result, no additional actions are required for this compiler.
+ +IBM XL C++ issues an unknown pragma warning (1540-1401) by default.
+ To disable this warning you can add the -qsuppress=1540-1401
+ command line option, for example:
+xlC -qsuppress=1540-1401 ... ++ + +
A pragma with the object
qualifier declares a C++ class
+ as a persistent object type. The qualifier can be optionally followed
+ by the table
specifier.
table
The table
specifier specifies the table name that should
+ be used to store objects of this class in a relational database. For
+ example:
+#pragma db object table("people") +class person +{ + ... +}; ++ +
If the table name is not specified, the class name is used as the + default.
+ +A pragma with the value
qualifier describes a value
+ type and can be optionally followed by the type
+ specifier.
type
The type
specifier specifies the native database type
+ that should be used for data members of this type. For example:
+#pragma db value(bool) type("INT NOT NULL") + +#pragma db object +class person +{ + ... +private: + bool married_; // Mapped to INT NOT NULL database type. + ... +}; ++ +
The ODB compiler includes the default mapping between common C++
+ types, such as bool
, int
, and
+ std::string
and the database types for each supported
+ database system. For more information on the default mapping,
+ refer to (@@ ref Database Systems).
In the above example we changed the mapping for the bool
+ type which is now mapped to the INT
database type. In
+ this case the value
pragma is all that is necessary
+ since the ODB compiler will be able to figure out how to store
+ a boolean value as an integer in the database. However, there
+ could be situations where the ODB compiler will not know how to
+ handle the conversion between the C++ and database representations
+ of a value. Consider, as an example, a situation where the
+ boolean value is stored in the database as a string:
+#pragma db value(bool) type("CHAR(5) NOT NULL") ++ +
The possible database value for the C++ true
value could
+ be "true"
, or "TRUE"
, or "True"
.
+ Or, maybe, all of the above are valid. The ODB compiler has no way
+ of knowing how your application wants to convert bool
+ to a string and back.
@@ value_type straits specialization example.
+ +It is also possible to change the database type mapping for individual + members, as discussed in (@@ ref member type specifier).
+ +A pragma with the member
qualifier or a positioned
+ pragma without a qualifier describes a data member. It can
+ be optionally followed, in any order, by the specifiers summarized
+ in the table below:
Specifier | +Summary | +Section | +
---|---|---|
id |
+ the member is an object id | +5.4.1 | +
auto |
+ id is assigned by the database | +5.4.2 | +
type |
+ the database type for the member | +5.4.3 | +
column |
+ the column name for the member | +5.4.4 | +
transient |
+ the member is not stored in the database | +5.4.5 | +
id
The id
specifier specifies that the data member contains
+ the object id. Every persistent class must have a member designated
+ as an object identifier. For example:
+#pragma db object +class person +{ + ... +private: + #pragma db id + std::string email_; + ... +}; ++ +
In a relational database, an identifier member is mapped to a + primary key.
+ +auto
The auto
specifier specifies that the object identifier
+ is automatically assigned by the database. Only a member that was
+ designated as an object identifier can have this specifier. For
+ example:
+#pragma db object +class person +{ + ... +private: + #pragma db id auto + unsigned long id_; + ... +}; ++ +
Note that automatically-assigned object identifiers are not reused.
+ If you have a high object turnover (that is, objects are routinely
+ made persistent and then erased), then care must be taken not to
+ run out of object identifiers. In such situations using
+ unsigned long long
as the identifier type is a safe
+ choice.
For additional information on the automatic identifier assignment, + refer to (@@ ref persist() function).
+ +type
The type
specifier specifies the native database type
+ that should be used for this data member. For example:
+#pragma db object +class person +{ + ... +private: + #pragma db type("INT NOT NULL") + bool married_; + ... +}; ++ +
The behavior of this specifier for members is similar to that
+ for value types. The only difference is the scope. The value
+ type pragma applies to all members with this value type that
+ don't have their own type
specifiers. While the
+ member pragma applies only to a single member. For more
+ information on the semantics of this specifier refer to
+ the (@@ ref value type "value" specifier).
column
The column
specifier specifies the column name
+ that should be used to store this member in a relational database.
+ For example:
+#pragma db object +class person +{ + ... +private: + #pragma db id column("person_id") + unsigned long id_; + ... +}; ++ +
If the column name is not specified, it is derived from the member
+ name by removing the common member name decorations, such as leading
+ and trailing underscores, the m_
prefix, etc.
transient
The transient
specifier instructs the ODB compiler
+ not to store the data member in the database. For example:
+#pragma db object +class person +{ + ... +private: + date born_; + + #pragma db transient + unsigned short age_; // Computed from born_. + ... +}; ++ +
This pragma is usualy used on computed members, pointers and + references that are only meaningful in the application's + memory, as well as utility members such as mutexes, etc.
+ + + + + +This chapter covers topics specific to the database system
+ implementations and their support in ODB. In particular, it
+ describes the system-specific database
classes
+ as well as the default mapping between basic C++ value types
+ and native database types.
To generate support code for the MySQL database you will need
+ to pass the "--database mysql
"
+ (or "-d mysql
") option to the ODB compiler.
+ Your application will also need to link to the ODB MySQL runtime
+ library (libodb-mysql
). All MySQL-specific ODB
+ classes are defined in the odb::mysql
namespace.
The following table summarizes the default mapping between basic + C++ value types and MySQL database types. This mapping can be + customized on the per-type and per-member basis using ODB pragmas + (@@ ref ODB Pragma language).
+ + +C++ Type | +MySQL type | +
---|---|
bool |
+ TINYINT(1) NOT NULL |
+
char |
+ TINYINT NOT NULL |
+
signed char |
+ TINYINT NOT NULL |
+
unsigned char |
+ TINYINT UNSIGNED NOT NULL |
+
short |
+ SMALLINT NOT NULL |
+
unsigned short |
+ SMALLINT UNSIGNED NOT NULL |
+
int |
+ INT NOT NULL |
+
unsigned int |
+ INT UNSIGNED NOT NULL |
+
long |
+ BIGINT NOT NULL |
+
unsigned long |
+ BIGINT UNSIGNED NOT NULL |
+
long long |
+ BIGINT NOT NULL |
+
unsigned long long |
+ BIGINT UNSIGNED NOT NULL |
+
float |
+ FLOAT NOT NULL |
+
double |
+ DOUBLE NOT NULL |
+
std::string |
+ TEXT NOT NULL/VARCHAR(255) NOT NULL |
+
Note that the std::string
type is mapped
+ differently depending on whether the member of this type
+ is an object id or not. If the member is an object id,
+ then for this member std::string
is mapped
+ to VARCHAR(255) NOT NULL
MySQL type. Otherwise
+ it is mapped to TEXT NOT NULL
.
The MySQL database
class has the following
+ interface:
+namespace odb +{ + namespace mysql + { + class database: public odb::database + { + public: + database (const char* user, + const char* passwd, + const char* db, + const char* host = 0, + unsigned int port = 0, + const char* socket = 0, + unsigned long client_flags = 0, + std::auto_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string& passwd, + const std::string& db, + const std::string& host = "", + unsigned int port = 0, + const std::string* socket = 0, + unsigned long client_flags = 0, + std::auto_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string* passwd, + const std::string& db, + const std::string& host = "", + unsigned int port = 0, + const std::string* socket = 0, + unsigned long client_flags = 0, + std::auto_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string& passwd, + const std::string& db, + const std::string& host, + unsigned int port, + const std::string& socket, + unsigned long client_flags = 0, + std::auto_ptr<connection_factory> = 0); + + database (const std::string& user, + const std::string* passwd, + const std::string& db, + const std::string& host, + unsigned int port, + const std::string& socket, + unsigned long client_flags = 0, + std::auto_ptr<connection_factory> = 0); + + database (int& argc, + char* argv[], + bool erase = false, + unsigned long client_flags = 0, + std::auto_ptr<connection_factory> = 0); + + static void + print_usage (std::ostream&); + + + public: + const char* + user () const; + + const char* + password () const; + + const char* + db () const; + + const char* + host () const; + + unsigned int + port () const; + + const char* + socket () const; + + unsigned long + client_flags () const; + + public: + details::shared_ptr<mysql::connection> + connection (); + }; + } +} ++ +
You will need to include the <odb/mysql/database.hxx>
+ header file to make this class available in your application.
The overloaded database
constructros allow you
+ to specify MySQL database parameters that should be used when
+ connecting to the database. In MySQL NULL
and
+ empty string are treated as the same values for all the
+ string parameters except password and socket. The
+ client_flags
argument allows you to specify
+ various MySQL client library flags. For more information
+ on the possible values, refer to the MySQL C API dicumentation.
+ The CLIENT_FOUND_ROWS
flag is always set by the
+ ODB MySQL runtime regardless of whether it was passed in the
+ client_flags
argument.
The last constructor variant extracts the database parameters + from the command line. The following options are recognized:
+ ++ --user <login> + --password <password> + --database <name> + --host <host> + --port <integer> + --socket <socket> + --options-file <file> ++ +
The --options-file
option allows you to specify some
+ or all of the database options in a file with each option appearing
+ on a separate line followed by space and an option value.
If the erase
argument to this constructor is true,
+ then the above options are removed from the argv
+ array and the argc
count is updated accordingly.
+ This is primarily useful if your application accepts other
+ options or arguments and you would like to get the MySQL
+ options out of the argv
array.
This constructor throws the odb::mysql::cli_exception
+ exception if the MySQL option values are missing or invalid.
+ See section (@@ ref MySQL Exceptions) for more information
+ on this exception.
The static print_usage()
function allows you
+ to print the list of options with short descriptions that
+ are recognized by this constructor.
The last argument to all of the constructor variants is the
+ pointer to the connection factory. If you pass a
+ non-NULL
value, the database instance assumes
+ ownership of the connection factory. The connection factory
+ interface as well as the available implementations are discussed
+ in the next section.
The set of accessor function following the constructors allows you
+ to query the parameters of the database
instance.
The connection()
function returns the MySQL database
+ connection encapsulated by the odb::mysql::connection
+ class. Normally, you wouldn't call this function directly and
+ instead let the ODB runtime manage database connections. However,
+ if for some reason you need to access the underlying MySQL connection
+ handle, refer to the ODB MySQL runtime source code for the interface
+ of the connection
class.
The connection_factory
abstract class has the
+ following interface:
+namespace odb +{ + namespace mysql + { + class connection_factory + { + public: + virtual void + database (mysql::database&) = 0; + + virtual details::shared_ptr<connection> + connect () = 0; + }; + } +} ++ +
The database()
function is called when a connection
+ factory is associated with a database instance. This happens in
+ the odb::mysql::database
class constructors. The
+ connect()
function is called whenever a database
+ connection is requested.
The two implementations of the connection_factory
+ interface provided by the ODB MySQL runtime are
+ the new_connection_factory
and
+ connection_pool_factory
. You will need to include
+ the <odb/mysql/connection-factory.hxx>
+ header file to make the connection_factory
interface
+ and these implementation classes available in your application.
The new_connection_factory
class creates a new
+ connection whenever one is requested. When a connection is no
+ longer needed it is released and closed. The
+ connection_pool_factory
class implements a
+ connection pool. It has the following interface:
+namespace odb +{ + namespace mysql + { + class connection_pool_factory: public connection_factory + { + connection_pool_factory (std::size_t max_connections = 0, + std::size_t min_connections = 0) + }; +}; ++ +
The max_connections
argument specifies the maximum
+ number and the of concurrent connections this pool factory will
+ maintain. Similarly, the min_connections
argument
+ specifies the minimum number of available connections that
+ should be kept open.
Whenever a connection is requested, the pool factory first
+ checks if there is an unused connection that can be returned.
+ If there is none, the pool factory checks the
+ max_connections
value to see if a new connection
+ can be created. If the total number of connections maintained
+ by the pool is less than this value, then a new connection is
+ created and returned. Otherwise the calling thread is blocked
+ until a connection becomes available.
When a connection is released, the pool factory first checks
+ if there are blocked threds waiting for a connection. If so,
+ one of them is unblocked and is given the connection. Otherwise,
+ the pool factory checks whether the total number of connections
+ maintained by the pool is greate than the min_connections
+ value. If that's the case, the connection is closed. Otherwise the
+ connection is added to the pool of available connections to be
+ returned on the next request. In other words, if the number of
+ connections maintained by the pool exceeds the min_connections
+ number and there are no threads waiting for a new connection,
+ then the pool will release the excess connections.
If max_connections
value is 0 then the pool will
+ create a new connection whenever all of the existing connections
+ are in use. If the min_connections
value is 0 then
+ the pool will never close a connection and instead maintain all
+ the connections that were ever created.
If you pass NULL
as the connection factory to
+ one of the database
constructors, then the
+ connection_pool_factory
instance will be
+ created by default with the min and max connections values
+ set to 0. The following code fragment shows how we can
+ pass our own connection factory instance:
+#include <odb/database.hxx> + +#include <odb/mysql/database.hxx> +#include <odb/mysql/connection-factory.hxx> + +int +main (int argc, char* argv[]) +{ + auto_ptr<odb::mysql::connection_factory> f ( + new odb::mysql::connection_pool_factory (20)); + + auto_ptr<odb::database> db ( + new mysql::database (argc, argv, false, 0, f)); +} ++ +
The ODB MySQL runtime library defines the following MySQL-specific + exceptions:
+ ++namespace odb +{ + namespace mysql + { + class database_exception: odb::database_exception + { + public: + unsigned int + error () const; + + const std::string& + sqlstate () const; + + const std::string& + message () const; + + virtual const char* + what () const throw (); + }; + + class cli_exception: odb::exception + { + public: + virtual const char* + what () const throw (); + }; + } +} ++ +
You will need to include the <odb/mysql/exceptions.hxx>
+ header file to make these exceptions available in your application.
The odb::mysql::database_exception
is thrown if
+ a MySQL database operation fails. The MySQL-specific error
+ information is accessible via the error()
,
+ sqlstate()
, and message()
functions.
+ All this information is also cobined and returned in
+ human-readable form by the what()
function.
The odb::mysql::cli_exception
is thrown by the
+ command line parsing constructor of the odb::mysql::database
+ class if the MySQL option values are missing or invalid. The
+ what()
function provides human-readable descriprion
+ of an error.