From 285a5f5af63a21d98758e065f2456fc39a3f4bc5 Mon Sep 17 00:00:00 2001
From: Boris Kolpackov person
which is saved in person.hxx
:
+// person.hxx // @@ -1132,7 +1132,7 @@ private: to save theperson
objects in a database. To achieve this we declare theperson
class as persistent: -+// person.hxx // @@ -1213,7 +1213,7 @@ private: have used that since each person is presumed to have a unique email address, for example: -+class person { ... @@ -1386,7 +1386,7 @@ mysql --user=odb_test --database=odb_test < person.sql database. In this section we will learn how to makeperson
objects persistent: -+// driver.cxx // @@ -1567,7 +1567,7 @@ mysql> quit each database operation. Here is how we can enable tracing just for the duration of our transaction: -+// Create a few persistent person objects. // { @@ -1611,7 +1611,7 @@ INSERT INTO `person` (`id`,`first`,`last`,`age`) VALUES (?,?,?,?) people from our database. To make it a bit more interesting, let's say hello only to people over 30: -+// driver.cxx // @@ -1711,7 +1711,7 @@ Hello, Jane! both sets are returned. We can change the line where we print the "Hello" string as follows to illustrate this point: -+cout << "Hello, " << i->first () << " (" << i->id () << ")!" << endl;@@ -1739,7 +1739,7 @@ Hello, Jane (8)! to change the object's state and then make these changes persistent. Let's illustrate this by updating Joe's age who just had a birthday: -+// driver.cxx // @@ -1829,7 +1829,7 @@ Hello, Joe! in the database, we can use the query facility to come up with an alternative implementation of the above transaction: -+// Joe Dirt just had a birthday, so update his age. An // alternative implementation without using the object id. // @@ -1885,7 +1885,7 @@ Hello, Joe! theperson_stat
view that returns the basic statistics about theperson
objects: -+#pragma db view object(person) struct person_stat { @@ -1905,7 +1905,7 @@ struct person_stat how we can load and print our statistics using the view we have just created: -+// Print some statistics about all the people in our database. // { @@ -1931,7 +1931,7 @@ struct person_stat re-run our example, then we will see the following additional lines in the output: -+count : 3 min age: 31 max age: 33 @@ -1943,7 +1943,7 @@ max age: 33 the persistent object from the database. The following code fragment shows how we can delete an object given its identifier: -+// John Doe is no longer in our database. // { @@ -1961,7 +1961,7 @@ max age: 33If we don't have an object id handy, we can use queries to find and delete the object:
-+// John Doe is no longer in our database. An alternative // implementation without using the object id. // @@ -2144,7 +2144,7 @@ max age: 33 it as such using thedb object
pragma, for example: -+#pragma db object class person { @@ -2156,7 +2156,7 @@ class person which designates one of the data members as an object id, for example: -+#pragma db object class person { @@ -2196,7 +2196,7 @@ class person<odb/core.hxx>
header, should be declared a friend of this object type. For example: -+#include <odb/core.hxx> #pragma db object @@ -2227,7 +2227,7 @@ private: to object types, composite value types have to be explicitly declared as persistent using thedb value
pragma, for example: -+#pragma db value class name { @@ -2321,7 +2321,7 @@ class name type. To specify the pointer type on the per object or per view basis we can use thedb pointer
pragma, for example: -+#pragma db object pointer(std::tr1::shared_ptr) class person { @@ -2332,7 +2332,7 @@ class personWe can also specify the default pointer for a group of objects or views at the namespace level:
-+#pragma db namespace pointer(std::tr1::shared_ptr) namespace accounting { @@ -2372,7 +2372,7 @@ namespace accounting thedb pointer
object or view pragma. For example: -+#pragma db object pointer(std::shared_ptr) namespace accounting { @@ -2419,7 +2419,7 @@ namespace accounting shows how we can create a database instance for the MySQL database system: -+#include <odb/database.hxx> #include <odb/mysql/database.hxx> @@ -2467,7 +2467,7 @@ auto_ptr<odb::database> db (odb::schema_catalog
class to create it in the database from within our application, for example: -+#include <odb/schema-catalog.hxx> odb::transaction t (db->begin ()); @@ -2484,7 +2484,7 @@ t.commit (); You will need to include the<odb/schema-catalog.hxx>
header file to make this class available in your application. -+namespace odb { class schema_catalog @@ -2569,7 +2569,7 @@ namespace odb header file to make this class available in your application. For example: -+#include <odb/transaction.hxx> transaction t (db.begin ()) @@ -2582,7 +2582,7 @@ t.commit ();The
-odb::transaction
class has the following interface:+namespace odb { class transaction @@ -2657,7 +2657,7 @@ namespace odb allow for more advanced use cases, such as multiplexing two or more transactions on the same thread. For example: -+transaction t1 (db1.begin ()); // Active transaction. transaction t2 (db2.begin (), false); // Not active. @@ -2688,7 +2688,7 @@ t2.commit (); and start a new one every time a certain number of database operations has been performed: -+transaction t (db.begin ()); for (size_t i (0); i < n; ++i) @@ -2718,7 +2718,7 @@ t.commit (); persistent objects only within the transaction scope. Consider, for example, these two implementations of the same transaction: -+void update_age (database& db, person& p) { @@ -2738,7 +2738,7 @@ update_age (database& db, person& p) alternative implementation which only instantiates theperson
object for the duration of the transaction: -+void update_age (database& db, unsigned long id) { @@ -2761,7 +2761,7 @@ update_age (database& db, unsigned long id) remains unchanged. One way to do this is to re-load the object's state from the database, for example: -+void update_age (database& db, person& p) { @@ -2807,7 +2807,7 @@ update_age (database& db, person& p) header file. Theodb::connection
class has the following interface: -+namespace odb { class connection @@ -2855,7 +2855,7 @@ namespace odbdatabase
instance. The following code fragment shows how we can obtain, use, and release a connection: -+using namespace odb::core; database& db = ... @@ -2939,7 +2939,7 @@ c->execute ("SET FOREIGN_KEY_CHECKS = 1");The following code fragment shows how to handle the recoverable exceptions by restarting the affected transaction:
-+const unsigned short max_retries = 5; for (unsigned short retry_count (0); ; retry_count++) @@ -2970,7 +2970,7 @@ for (unsigned short retry_count (0); ; retry_count++) to make a transient instance persistent. This function has four overloaded versions with the following signatures: -+template <typename T> typename object_traits<T>::id_type persist (const T& object); @@ -3022,7 +3022,7 @@ for (unsigned short retry_count (0); ; retry_count++) deduced from the argument being passed. The following example shows how we can call these functions: -+person john ("John", "Doe", 33); shared_ptr<person> jane (new person ("Jane", "Doe", 32)); @@ -3056,7 +3056,7 @@ cerr << "Jane's id: " << jane_id << endl; function template. This function has two overloaded versions with the following signatures: -+template <typename T> typename object_traits<T>::pointer_type load (const typename object_traits<T>::id_type& id); @@ -3078,7 +3078,7 @@ cerr << "Jane's id: " << jane_id << endl; the second function because the object type will be automatically deduced from the second argument, for example: -+transaction t (db.begin ()); auto_ptr<person> jane (db.load<person> (jane_id)); @@ -3095,7 +3095,7 @@ t.commit (); has a number of special properties. This function has two overloaded versions with the following signatures: -+template <typename T> void reload (T& object); @@ -3133,7 +3133,7 @@ t.commit (); is persistent, we can use thefind()
function instead ofload()
, for example: -+template <typename T> typename object_traits<T>::pointer_type find (const typename object_traits<T>::id_type& id); @@ -3162,7 +3162,7 @@ t.commit (); function template. This function has three overloaded versions with the following signatures: -+template <typename T> void update (const T& object); @@ -3186,7 +3186,7 @@ t.commit (); in the earlier section on transactions. It uses the hypotheticalbank_account
persistent class: -+void transfer (database& db, unsigned long from_acc, @@ -3218,7 +3218,7 @@ transfer (database& db, and theupdate()
function with object pointer argument, for example: -+transaction t (db.begin ()); shared_ptr<bank_account> from (db.load<bank_account> (from_acc)); @@ -3276,7 +3276,7 @@ t.commit (); erased object, this instance becomes transient. Theerase()
function has the following overloaded versions: -+template <typename T> void erase (const T& object); @@ -3310,7 +3310,7 @@ t.commit (); deduced from their arguments. The following example shows how we can call these functions: -+person& john = ... shared_ptr<jane> jane = ... unsigned long joe_id = ... @@ -3345,7 +3345,7 @@ t.commit (); specified. Theerase_query()
function has the following overloaded versions: -+template <typename T> unsigned long long erase_query (); @@ -3363,7 +3363,7 @@ t.commit (); theerase_query()
function, we have to explicitly specify the object type we are erasing. For example: -+typedef odb::query<person> query; transaction t (db.begin ()); @@ -3389,7 +3389,7 @@ t.commit (); "Relationships" for complete definitions of these classes. -+typedef odb::query<employee> query; transaction t (db.begin ()); @@ -3413,7 +3413,7 @@ t.commit ();database::execute()
function, which has three overloaded versions, provides this functionality: -+unsigned long long execute (const char* statement); @@ -3432,7 +3432,7 @@ t.commit (); return the number of rows that were affected by the statement. For example: -+transaction t (db.begin ()); db.execute ("DROP TABLE test"); @@ -3464,7 +3464,7 @@ t.commit (); extract such a subset of statements from the database logs, it is easy to achieve with ODB tracing support: -+transaction t (db.begin ()); t.tracer (stderr_tracer); @@ -3485,7 +3485,7 @@ t.commit (); andodb::transaction
) provide the identical tracing API: -+void tracer (odb::tracer&); @@ -3520,7 +3520,7 @@ t.commit (); Theodb::tracer
interface provided the following callback functions: -+namespace odb { class tracer @@ -3567,7 +3567,7 @@ namespace odb information can be obtained from theodb::pgsql::statement
object: -+#include <odb/pgsql/tracer.hxx> #include <odb/pgsql/database.hxx> #include <odb/pgsql/connection.hxx> @@ -3605,7 +3605,7 @@ class pgsql_tracer: public odb::pgsql::tracerNote also that you can only set a database-specific tracer object using a database-specific database instance, for example:
-+pgsql_tracer tracer; odb::database& db = ...; @@ -3628,7 +3628,7 @@ db.tracer (tracer); // Ok. fromstd::exception
and has the following interface: -+namespace odb { struct exception: std::exception @@ -3647,7 +3647,7 @@ namespace odbThe concrete exceptions that can be thrown by ODB are presented in the following listing:
-+namespace odb { struct null_pointer: exception @@ -3891,7 +3891,7 @@ namespace odb ordinary C++. We have already seen examples of these queries in the introductory chapters. Below is another, more interesting, example: -+typedef odb::query<person> query; typedef odb::result<person> result; @@ -3914,14 +3914,14 @@ namespace odb is the re-implementation of the above example using SQL as the native query language: -+query q ("first = 'John' AND age = " + query::_ref (age));Note that at this level we lose the static typing of query expressions. For example, if we wrote something like this:
-+query q (query::first == 123 && query::agee < query::_ref (age));@@ -3931,7 +3931,7 @@ namespace odbquery::agee
. On the other hand, if we wrote something like this: -+query q ("first = 123 AND agee = " + query::_ref (age));@@ -3941,7 +3941,7 @@ namespace odbWe can also combine the two query languages in a single query, for example:
-+query q ("first = 'John'" + (query::age < query::_ref (age)));@@ -3954,7 +3954,7 @@ namespace odb combined with logical operators such as&&
(AND),||
(OR), and!
(NOT). For example: -+typedef odb::query<person> query; query q (query::first == "John" || query::age == 31); @@ -4051,7 +4051,7 @@ namespace odb excluding theend
position. The following code fragment shows how we can use these functions: -+std::vector<string> names; names.push_back ("John"); @@ -4069,7 +4069,7 @@ namespace odb make sure the expression is evaluated in the desired order. For example: -+query q ((query::first == "John" || query::first == "Jane") && query::age < 31);@@ -4090,7 +4090,7 @@ namespace odb at the query execution time. Consider, for example, the following two queries: -+string name ("John"); query q1 (query::first == query::_val (name)); @@ -4110,7 +4110,7 @@ namespace odb native query language, binding must always be specified explicitly. For example: -+query q1 (query::age < age); // By value. query q2 (query::age < query::_val (age)); // By value. query q3 (query::age < query::_ref (age)); // By reference. @@ -4134,7 +4134,7 @@ namespace odbdatabase::query()
function template. It has two overloaded versions: -+template <typename T> result<T> query (bool cache = true); @@ -4156,7 +4156,7 @@ namespace odbWhen calling the
-query()
function, we have to explicitly specify the object type we are querying. For example:+typedef odb::query<person> query; typedef odb::result<person> result; @@ -4168,7 +4168,7 @@ namespace odb query variable before executing it. For example, the following two queries are equivalent: -+query q (query::first == "John"); result r1 (db.query<person> (q)); @@ -4189,7 +4189,7 @@ namespace odbIt is also possible to create queries from other queries by combining them using logical operators. For example:
-+result find_minors (database& db, const query& name_query) { @@ -4205,7 +4205,7 @@ result r (find_minors (db, query::first == "John")); matching the query criteria. The result is returned as an instance of theodb::result
class template, for example: -+typedef odb::query<person> query; typedef odb::result<person> result; @@ -4225,7 +4225,7 @@ result r (find_minors (db, query::first == "John")); standard C++ sequence requirements and has the following interface: -+namespace odb { template <typename T> @@ -4306,7 +4306,7 @@ namespace odb together with theodb::result<T>::iterator
type, for example: -+result r (db.query<person> (query::first == "John")); for (result::iterator i (r.begin ()); i != r.end (); ++i) @@ -4318,7 +4318,7 @@ namespace odbIn C++11 we can use the
-auto
-typed variabe instead of spelling the iterator type explicitly, for example:+for (auto i (r.begin ()); i != r.end (); ++i) { ... @@ -4328,7 +4328,7 @@ namespace odbThe C++11 range-based
-for
-loop can be used to further simplify the iteration:+for (person& p: r) { ... @@ -4350,7 +4350,7 @@ namespace odbThe result iterator has the following dereference functions that can be used to access the pointed-to object:
-+namespace odb { template <typename T> @@ -4384,7 +4384,7 @@ namespace odb operators until it is advanced to the next object or we call the firstload()
function (see below). For example: -+result r (db.query<person> (query::first == "John")); for (result::iterator i (r.begin ()); i != r.end ();) @@ -4406,7 +4406,7 @@ namespace odb This allows us to write code like this without worrying about a double allocation: -+result r (db.query<person> (query::first == "John")); for (result::iterator i (r.begin ()); i != r.end (); ++i) @@ -4427,7 +4427,7 @@ namespace odb us to load the current object's state into an existing instance. For example: -+result r (db.query<person> (query::first == "John")); person p; @@ -4445,7 +4445,7 @@ namespace odb create the object. This can be useful when all we need is the object's identifier. For example: -+std::set<unsigned long> set = ...; // Persons of interest. result r (db.query<person> (query::first == "John")); @@ -4482,7 +4482,7 @@ namespace odbWe don't need to do anything special to declare a member of a container type in a persistent class. For example:
-+#pragma db object class person { @@ -4530,7 +4530,7 @@ private: case theodb::access
class should be made a friend of the value or key type. For example: -+#pragma db value class name { @@ -4576,7 +4576,7 @@ private:Consider the following persistent object as an example:
-+#pragma db object class person { @@ -4603,7 +4603,7 @@ private: Language". The following example shows some of the possible customizations: -+#pragma db object class person { @@ -4629,7 +4629,7 @@ private: "unordered
", Section 12.4.14, "unordered
"). For example: -+#pragma db object class person { @@ -4667,7 +4667,7 @@ private:Consider the following persistent object as an example:
-+#pragma db object class person { @@ -4693,7 +4693,7 @@ private: Language". The following example shows some of the possible customizations: -+#pragma db object class person { @@ -4730,7 +4730,7 @@ private:Consider the following persistent object as an example:
-+#pragma db object class person { @@ -4757,7 +4757,7 @@ private: Language". The following example shows some of the possible customizations: -+#pragma db object class person { @@ -4854,7 +4854,7 @@ private: will use theshared_ptr
andweak_ptr
smart pointers from the TR1 (std::tr1
) namespace. -+#pragma db object class employer { @@ -4888,7 +4888,7 @@ class employee 12.4.19, "value_null
/value_not_null
") for containers of object pointers. For example: -+#pragma db object class employee { @@ -4912,7 +4912,7 @@ class employee relationship between two persistent objects, as shown in the following code fragment: -+// Create an employer and a few employees. // unsigned long john_id, jane_id; @@ -4981,7 +4981,7 @@ unsigned long john_id, jane_id; objects. For example, the following transaction finds all the employees of Example Inc that have the Doe last name: -+typedef odb::query<employee> query; typedef odb::result<employee> result; @@ -5004,7 +5004,7 @@ t.commit (); all theemployee
objects that don't have an associatedemployer
object: -+result r (db.query<employee> (query::employer.is_null ()));@@ -5051,7 +5051,7 @@ result r (db.query<employee> (query::employer.is_null ())); employee-employer relationship (an employee has one employer). The following persistent C++ classes model this relationship: -+#pragma db object class employer { @@ -5092,7 +5092,7 @@ CREATE TABLE employee ( in multiple projects). The following persistent C++ classes model this relationship: -+#pragma db object class project { @@ -5133,7 +5133,7 @@ CREATE TABLE employee_projects ( and columns above can be customized using ODB pragmas (Chapter 12, "ODB Pragma Language"). For example: -+#pragma db object class employee { @@ -5164,7 +5164,7 @@ CREATE TABLE employee_projects ( be used as one of the pointers to avoid ownership cycles. For example: -+class employee; #pragma db object @@ -5197,7 +5197,7 @@ class employee provide a single function that updates both pointers at the same time. For example: -+#pragma db object class position: public enable_shared_from_this<position> { @@ -5234,7 +5234,7 @@ private: transaction that tries to load theposition
object from the above example without using a session: -+transaction t (db.begin ()) shared_ptr<position> p (db.load<position> (1)); ... @@ -5257,7 +5257,7 @@ t.commit ();As the exception name suggests, the easiest way to resolve this problem is to use a session:
-+session s; transaction t (db.begin ()) shared_ptr<position> p (db.load<position> (1)); @@ -5303,7 +5303,7 @@ CREATE TABLE employee ( a pointer is the inverse side of a bidirectional relationship. Either side of a relationship can be made inverse. For example: -+#pragma db object class position { @@ -5362,7 +5362,7 @@ CREATE TABLE employee ( fills one position and a position is filled by one employee). The following persistent C++ classes model this relationship: -+class employee; #pragma db object @@ -5420,7 +5420,7 @@ CREATE TABLE employee ( employees and an employee is employed by one employer). The following persistent C++ classes model this relationship: -+class employee; #pragma db object @@ -5485,7 +5485,7 @@ CREATE TABLE employee ( projects and a project can have multiple participating employees). The following persistent C++ classes model this relationship: -+class employee; #pragma db object @@ -5547,7 +5547,7 @@ CREATE TABLE employee (Consider again the bidirectional, one-to-many employer-employee relationship that was presented earlier in this chapter:
-+class employee; #pragma db object @@ -5578,7 +5578,7 @@ class employeeConsider also the following transaction which obtains the employer name given the employee id:
-+unsigned long id = ... string name; @@ -5638,7 +5638,7 @@ t.commit (); relationship to use lazy pointers. Here we choose to use lazy pointers for both sides of the relationship. -+class employee; #pragma db object @@ -5662,7 +5662,7 @@ class employeeAnd the transaction is changed like this:
-+unsigned long id = ... string name; @@ -5687,7 +5687,7 @@ t.commit (); lazy loading functionality. Overall, the interface of a lazy pointer follows this general outline: -+template <class T> class lazy_ptr { @@ -5751,7 +5751,7 @@ public: theemployer
andemployee
classes presented earlier. -+typedef std::vector<lazy_weak_ptr<employee> > employees; session s; @@ -5795,7 +5795,7 @@ t.commit (); to Example Inc. The straightforward implementation of this transaction is presented below: -+session s; transaction t (db.begin ()); @@ -5820,7 +5820,7 @@ t.commit (); unloaded lazy pointer with the database where the object is stored as well as its identifier: -+lazy_shared_ptr<employer> er (db, std::string ("Example Inc")); shared_ptr<employee> e (new employee ("John", "Doe")); @@ -5924,7 +5924,7 @@ t.commit (); a composite value type we use thedb value
pragma, for example: -+#pragma db value class basic_name { @@ -5947,7 +5947,7 @@ class basic_name an element of a container, then theodb::access
class should be declared a friend of this value type. For example: -+#pragma db value class basic_name { @@ -5981,7 +5981,7 @@ private: are not allowed. The following example illustrates some of the possible use cases: -+#pragma db value class basic_name { @@ -6023,7 +6023,7 @@ class personA composite value type can also be defined as an instantiation of a C++ class template, for example:
-+template <typename T> struct point { @@ -6052,7 +6052,7 @@ class objectstd::pair
defined in theutility
standard header file: -+#include <utility> // std::pair typedef std::pair<std::string, std::string> phone_numbers; @@ -6083,7 +6083,7 @@ class person expression while querying the database for theperson
objects. For example: -+typedef odb::query<person> query; typedef odb::result<person> result; @@ -6101,7 +6101,7 @@ t.commit ();An object id can be of a composite value type, for example:
-+#pragma db value class name { @@ -6138,7 +6138,7 @@ class person types things are slightly more complex since they are mapped to multiple columns. Consider the following example: -+#pragma db value class name { @@ -6178,7 +6178,7 @@ CREATE TABLE person (db column
pragma as shown in the following example: -+#pragma db value class name { @@ -6212,7 +6212,7 @@ CREATE TABLE person (We can also make the column prefix empty, for example:
-+#pragma db object class person { @@ -6246,7 +6246,7 @@ CREATE TABLE person ( column names, except that by default both the object name and the member name are used as a prefix. For example: -+#pragma db value class name { @@ -6284,7 +6284,7 @@ CREATE TABLE person (db table
pragma (Section 12.4.15, "table
"), for example: -+#pragma db value class name { @@ -6344,7 +6344,7 @@ CREATE TABLE person_nickname ( theNULL
values for data members of theodb::nullable
type. For example: -+#include <odb/nullable.hxx> #pragma db object @@ -6362,7 +6362,7 @@ class person in the<odb/nullable.hxx>
header file and has the following interface: -+namespace odb { template <typename T> @@ -6408,7 +6408,7 @@ namespace odbThe following example shows how we can use this interface:
-+nullable<string> ns; // Using the accessor interface. @@ -6459,7 +6459,7 @@ namespace odb need to enable it explicitly using thedb null
pragma. For example: -+#pragma db object class person { @@ -6487,7 +6487,7 @@ class person value is translated toNULL
values for all the simple data members of this composite value. For example: -+#pragma db value struct name { @@ -6511,7 +6511,7 @@ class person only restriction is that these pointers must not be