From dce27fe8994e819a463e83212779d3d43bd2ba2e Mon Sep 17 00:00:00 2001
From: Boris Kolpackov
- 6.3 Lazy Pointers
+ 6.4 Using Custom Smart Pointers
+ 6.3 Circular Relationships
+ 6.4 Lazy Pointers
@@ -2537,7 +2538,7 @@ namespace accounting
provide support for smart pointers found in these frameworks and
libraries (Part III, "Profiles"). It is also
easy to add support for our own smart pointers, as described in
- Section 6.4, "Using Custom Smart Pointers".6.5 Using Custom Smart Pointers
shared_ptr
/weak_ptr
(TR1 or C++11),
std::unique_ptr
(C++11),
std::auto_ptr
, and raw pointers. Plus, ODB profile
- libraries, that available for commonly used frameworks and libraries
+ libraries, that are available for commonly used frameworks and libraries
(such as Boost and Qt), provide support for smart pointers found in these
frameworks and libraries (Part III, "Profiles"). It is
also easy to add support for a custom smart pointer as discussed later
- in Section 6.4, "Using Custom Smart Pointers". Any
+ in Section 6.5, "Using Custom Smart Pointers". Any
supported smart pointer can be used in a data member as long as it can be
explicitly constructed from the canonical object pointer
(Section 3.3, "Object and View Pointers"). For
@@ -4994,7 +4995,7 @@ private:
is not loaded automatically when the containing object is loaded.
Instead, we have to explicitly request the instantiation of the
pointed-to object. Lazy pointers are discussed in
- detail in Section 6.3, "Lazy Pointers".
+ detail in Section 6.4, "Lazy Pointers".
As a simple example, consider the following employee-employer
relationship. Code examples presented in this chapter
@@ -5416,7 +5417,7 @@ t.commit ();
loaded employee
object preventing its immediate
deletion. Another way to resolve this problem is to avoid
immediate loading of the pointed-to objects using lazy weak
- pointers. Lazy pointers are discussed in Section 6.3,
+ pointers. Lazy pointers are discussed in Section 6.4,
"Lazy Pointers" later in this chapter.
Above, to model a bidirectional relationship in persistent classes, @@ -5689,7 +5690,160 @@ CREATE TABLE employee ( id BIGINT UNSIGNED NOT NULL PRIMARY KEY); -
A relationship between two persistent classes is circular if each
+ of them references the other. Bidirectional relationships are
+ always circular. A unidirectional relationship combined with
+ inheritance (Chapter 8, "Inheritance") can also
+ be circular. For example, the employee
class could
+ derive from person
which, in turn, could contain a
+ pointer to employee
.
We don't need to do anything extra if persistent classes with + circular dependencies are defined in the same header + file. Specifically, ODB will make sure that the database tables + and foreign key constraints are created in the correct order. As a + result, unless you have good reasons not to, it is recommended that + you keep persistent classes with circular dependencies in the same + header file.
+ +If you have to keep such classes in separate header files, then + there are two extra steps that you may need to take in order to + use these classes with ODB. Consider again the example from + Section 6.2.1, "One-to-One Relationships" + but this time with the classes defined in separate headers:
+ ++// position.hxx +// +class employee; + +#pragma db object +class position +{ + ... + + #pragma db id + unsigned long id_; + + #pragma db inverse(position_) + weak_ptr<employee> employee_; +}; ++ +
+// employee.hxx +// +#include "position.hxx" + +#pragma db object +class employee +{ + ... + + #pragma db id + unsigned long id_; + + #pragma db not_null + shared_ptr<position> position_; +}; ++ +
Note that the position.hxx
header contains only the forward
+ declaration for employee
. While this is sufficient to
+ define a valid, from the C++ point of view, position
class,
+ the ODB compiler needs to "see" the definitions of the pointed-to
+ persistent classes. There are several ways we can fulfil this
+ requirement. The easiest is to simply include employee.hxx
+ at the end of position.hxx
:
+// position.hxx +// +class employee; + +#pragma db object +class position +{ + ... +}; + +#include "employee.hxx" ++ +
We can also limit this inclusion only to the time when
+ position.hxx
is compiled with the ODB compiler:
+// position.hxx +// + +... + +#ifdef ODB_COMPILER +# include "employee.hxx" +#endif ++ +
Finally, if we don't want to modify position.hxx
,
+ then we can add employee.hxx
to the ODB compilation
+ process with the --odb-epilogue
option. For example:
+odb ... --odb-epilogue "#include \"employee.hxx\"" position.hxx ++ +
Note also that in this example we didn't have to do anything extra
+ for employee.hxx
because it already includes
+ position.hxx
. However, if instead it relied only
+ on the forward declaration of the position
class,
+ then we would have to handle it in the same way as
+ position.hxx
.
The other difficulty with separately defined classes involving
+ circular relationships has to do with the correct order of foreign
+ key constraint creation in the generated database schema. In
+ the above example, if we generate the database schema as
+ standalone SQL files, then we will end up with two such files:
+ position.sql
and employee.sql
.
+ If we try to execute employee.sql
first, then
+ we will get an error indicating that the table corresponding to
+ the position
class and referenced by the foreign
+ key constraint corresponding to the position_
+ pointer does not yet exist.
Note that there is no such problem if the database schema + is embedded in the generated C++ code instead of being produced + as standalone SQL files. In this case, the ODB compiler is + able to ensure the correct creation order even if the classes + are defined in separate header files.
+ +In certain cases, for example, a bidirectional relationship
+ with an inverse side, this problem can be resolved by executing
+ the database schema creation files in the correct order. In our
+ example, this would be position.sql
first
+ and employee.sql
second. However, this approach
+ doesn't scale beyond simple object models.
A more robust solution to this problem is to generate the database
+ schema for all the persistent classes into a single SQL file. This
+ way, the ODB compiler can again ensure the correct creation order
+ of tables and foreign keys. To instruct the ODB compiler to produce
+ a combined schema file for several headers we can use the
+ --generate-schema-only
and --at-once
+ options. For example:
+odb ... --generate-schema-only --at-once --output-name schema \ +position.hxx employee.hxx ++ +
The result of the above command is a single schema.sql
+ file that contains the database creation code for both
+ position
and employee
classes.
Consider again the bidirectional, one-to-many employer-employee relationship that was presented earlier in this chapter:
@@ -5981,7 +6135,7 @@ db.persist (e); t.commit (); -While the ODB runtime and profile libraries provide support for the majority of widely-used pointers, it is also easy to add @@ -8654,7 +8808,7 @@ t.commit ();
The per-object caching policies depend on the object pointer kind
- (Section 6.4, "Using Custom Smart Pointers").
+ (Section 6.5, "Using Custom Smart Pointers").
Objects with a unique pointer, such as std::auto_ptr
or std::unique_ptr
, as an object pointer are never
cached since it is not possible to have two such pointers pointing
@@ -17205,7 +17359,7 @@ class person
odb::boost::lazy_weak_ptr
. You will need to include the
<odb/boost/lazy-ptr.hxx>
header file to make the lazy
variants available in your application. For the description of the lazy
- pointer interface and semantics refer to Section 6.3,
+ pointer interface and semantics refer to Section 6.4,
"Lazy Pointers". The following example shows how we can use these
smart pointers to establish a relationship between persistent objects.
QLazyWeakPointer
. You will need to include the
<odb/qt/lazy-ptr.hxx>
header file to make the lazy
variants available in your application. For the description of the lazy
- pointer interface and semantics refer to Section 6.3,
+ pointer interface and semantics refer to Section 6.4,
"Lazy Pointers". The following example shows how we can use these
smart pointers to establish a relationship between persistent objects.
--
cgit v1.1