From 6a4faf610de03a270c45379c84a36bc228ba739e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 30 Nov 2010 17:00:18 +0200 Subject: Add chapter on containers --- doc/manual.xhtml | 361 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 354 insertions(+), 7 deletions(-) diff --git a/doc/manual.xhtml b/doc/manual.xhtml index e14fdc7..fdc60c0 100644 --- a/doc/manual.xhtml +++ b/doc/manual.xhtml @@ -2287,6 +2287,354 @@ namespace odb +

X Containers

+ +

The ODB runtime library provides built-in persistence support for + all commonly used standard C++ containers, namely, + std::vector, std::list, std::set, + std::multiset, std::map, and + std::multimap. Plus, ODB profile libraries are available + for commonly used frameworks and libraries (such as Boost and Qt) + that provide persistence support for containers found in these + frameworks and libraries. It is also easy to persist custom + container types as discussed later in Section X.4, + "Using Custom Containers".

+ +

You 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
+{
+  ...
+private:
+  std::vector<std::name> nicknames_;
+  ...
+};
+  
+ +

A data member in a persistent class that is of a container type + behaves like a value type. That is, when an object is made persistent, + the elements of the container are store in the database. Similarly, + when a persistent object is loaded from the database, the contents + of the container are automatically loaded as well.

+ +

While an ordinary member is mapped to one or more columns in the + object's table, a member of a container type is mapped to a seperate + table. The exact schema of such a table depends on the kind of + a container. ODB defines the following container kinds: ordered, + set, multiset, map, and multimap. The container kinds and the + contents of the tables to which they are mapped are discussed + in detail in the following sections.

+ +

Containers in ODB can contain simple value types, composite value + types (see @@), and object pointers (see @@). Containers of + containers are not supported. A key in map and multimap + containers can be a simple or composite value type but not + an object pointer. An index in the ordered container should + be a simple integer type.

+ +

The value type in the ordered, set, and map containers as well as + the key type in the map containers should be default-constructible. + The default constructor in these types can be made private in which + case the odb::access class should be made a friend of + the value or key type. For example:

+ +
+#pragma db value
+class name
+{
+public:
+  name (const std::string&, const std::string&);
+  ...
+private:
+  friend class odb::access;
+  name ();
+  ...
+};
+
+#pragma db object
+class person
+{
+  ...
+private:
+  #pragma db id auto
+  unsigned long id_;
+
+  std::vector<name> aliases_;
+  ...
+};
+  
+ + +

X.1 Ordered Containers

+ +

In ODB an ordered container is any container that maintains (explicitly + or implicitly) an order of its elements in the form of an integer index. + Standard C++ containers that are ordered include std::vector + and std::list. While elements in std::set + are also kept in a specific order, this order is not based on an + integer index but rather on the relationship between elements. As + a result, std::set is not considered an ordered + container for the purpose of persistence.

+ +

The database table for an ordered container consists of at least + three columns. The first column contains the object id of a + persistent class instance of which the container is a member. + The second column contains the element index within a container. + And the last column contains the element value. If the object + id or element value are composite, then instead of a single + column they can occupy multiple columns.

+ +

Consider the following persistent object as an example:

+ +
+#pragma db object
+class person
+{
+  ...
+private:
+  #pragma db id auto
+  unsigned long id_;
+
+  std::vector<std::string> nicknames_;
+  ...
+};
+  
+ +

The resulting database table (called person_nicknames) will + contain the object id column of type unsigned long + (called object_id), the index column of an integer type + (called index), and the value column of type + std::string (called value).

+ +

A number of ODB pragmas allow you to customize the table name, + column names, and native database types for the container both on + the per-container and per-member basis. For more information on + these pragmas, refer to Chapter 5, "ODB Pragma + Language". The following example shows some of the possible + customizations:

+ +
+#pragma db object
+class person
+{
+  ...
+private:
+  #pragma db id auto
+  unsigned long id_;
+
+  #pragma db table("nicknames")                        \
+             id_column ("person_id")                   \
+             index_type ("SMALLINT UNSIGNED NOT NULL") \
+             index_column ("nickname_number")          \
+             value_type ("VARCHAR(255) NOT NULL")      \
+             value_column ("nickname")
+  std::vector<std::string> nicknames_;
+  ...
+};
+  
+ +

While the C++ container used in the persistent class may be ordered, + sometimes we may wish to store such a container in the database without + the order information. In the example above, for instance, the order + of person's nicknames is probably not important. To instruct the ODB + compiler to ignore the order in ordered containers we can use the + unordered pragma (see Chapter 5, "ODB + Pragma Language" for details). For example:

+ +
+#pragma db object
+class person
+{
+  ...
+private:
+  #pragma db id auto
+  unsigned long id_;
+
+  #pragma db unordered
+  std::vector<std::string> nicknames_;
+  ...
+};
+  
+ +

The table for the ordered container that is marked unordered will + miss the index column and the order in which elements are retrieved + from the database may not be the same as the order in which they + were stored.

+ +

X.2 Set and Multiset Containers

+ +

In ODB set and multiset containers (referred to as just set + containers) are associative containers that contain elements + based on some relationship between them. A set container may + or may not guarantee a particular order of the elements that + it stores. Standard C++ containers that are considered set + containers for the purpose of persistence include + std::set and std::multiset.

+ +

The database table for a set container consists of at least + two columns. The first column contains the object id of a + persistent class instance of which the container is a member. + And the second column contains the element value. If the object + id or element value are composite, then instead of a single + column they can occupy multiple columns.

+ +

Consider the following persistent object as an example:

+ +
+#pragma db object
+class person
+{
+  ...
+private:
+  #pragma db id auto
+  unsigned long id_;
+
+  std::set<std::string> emails_;
+  ...
+};
+  
+ +

The resulting database table (called person_emails) will + contain the object id column of type unsigned long + (called object_id) and the value column of type + std::string (called value).

+ +

A number of ODB pragmas allow you to customize the table name, + column names, and native database types for the container both on + the per-container and per-member basis. For more information on + these pragmas, refer to Chapter 5, "ODB Pragma + Language". The following example shows some of the possible + customizations:

+ +
+#pragma db object
+class person
+{
+  ...
+private:
+  #pragma db id auto
+  unsigned long id_;
+
+  #pragma db table("emails")                      \
+             id_column ("person_id")              \
+             value_type ("VARCHAR(255) NOT NULL") \
+             value_column ("email")
+  std::set<std::string> emails_;
+  ...
+};
+  
+ +

X.3 Map and Multimap Containers

+ +

In ODB map and multimap containers (referred to as just set + containers) are associative containers that contain key-value + elemenst based on some relationship between keys. A map container + may or may not guarantee a particular order of the elements that + it stores. Standard C++ containers that are considered map + containers for the purpose of persistence include + std::map and std::multimap.

+ +

The database table for a map container consists of at least + three columns. The first column contains the object id of a + persistent class instance of which the container is a member. + The second column contains the element key. And the last column + contains the element value. If the object id, element key, or + element value are composite, then instead of a single column + they can occupy multiple columns.

+ +

Consider the following persistent object as an example:

+ +
+#pragma db object
+class person
+{
+  ...
+private:
+  #pragma db id auto
+  unsigned long id_;
+
+  std::map<unsigned short, float> age_weight_map_;
+  ...
+};
+  
+ +

The resulting database table (called person_age_weight_map) + will contain the object id column of type unsigned long + (called object_id), the key column of type + unsigned short (called key), and the value + column of type std::string (called value).

+ +

A number of ODB pragmas allow you to customize the table name, + column names, and native database types for the container both on + the per-container and per-member basis. For more information on + these pragmas, refer to Chapter 5, "ODB Pragma + Language". The following example shows some of the possible + customizations:

+ +
+#pragma db object
+class person
+{
+  ...
+private:
+  #pragma db id auto
+  unsigned long id_;
+
+  #pragma db table("weight_map")                \
+             id_column ("person_id")            \
+             key_type ("INT UNSIGNED NOT NULL") \
+             key_column ("age")                 \
+             value_type ("DOUBLE NOT NULL")     \
+             value_column ("weight")
+  std::map<unsigned short, float> age_weight_map_;
+  ...
+};
+  
+ +

X.4 Using Custom Containers

+ +

While the ODB runtime and profile libraries provide support for + a wide range of containers, it is also easy to persist custom + container types.

+ +

To achieve this you will need to implement the + container_traits class template specialization for + your container. First determine the container kind (ordered, set, + multiset, map, or multimap) for your container type. Then use a + specialization for one of the standard C++ containers found in + the common ODB runtime library (libodb) as a base + for your own implementation.

+ +

Once the container traits specialization is ready for your container, + you will need to include it into the ODB compilation process using + the --odb-epilogue option and into the generated header + file with the --hxx-prologue option. As an example, + suppose we have a hash table container for which we have the traits + specialization implemented in the hashtable-traits.hxx + file. Then, we can create an ODB compiler options file for this + container and save it to hashtable.options:

+ +
+# Options file for the hash table container.
+#
+--odb-epilogue '#include "hashtable-traits.hxx"'
+--hxx-prologue '#include "hashtable-traits.hxx"'
+  
+ +

Now, whenever we compile a header file that uses the hashtable + container, we can pass the following option to make sure it + is recognized by the ODB compiler as a container and the traits + file is included in the generated code:

+ +
+--options-file hashtable.options
+  
+ + +

4 Querying the Database

@@ -2359,7 +2707,6 @@ namespace odb query q ("first = 'John'" + (query::age < query::_ref (age))); -

4.1 ODB Query Language

An ODB query is an expression that tells the database system whether @@ -3344,7 +3691,7 @@ typedef std::vector<std::string> names; example:

-typedef std::map<unsigned short, double> age_weight_map;
+typedef std::map<unsigned short, float> age_weight_map;
 #pragma db value(age_weight_map) key_type("INT UNSIGNED NOT NULL")
   
@@ -3397,7 +3744,7 @@ typedef std::vector<std::string> names; container's table. For example:

-typedef std::map<unsigned short, double> age_weight_map;
+typedef std::map<unsigned short, float> age_weight_map;
 #pragma db value(age_weight_map) key_column("age")
   
@@ -3411,7 +3758,7 @@ typedef std::map<unsigned short, double> age_weight_map; container's table. For example:

-typedef std::map<unsigned short, double> age_weight_map;
+typedef std::map<unsigned short, float> age_weight_map;
 #pragma db value(age_weight_map) value_column("weight")
   
@@ -3774,7 +4121,7 @@ class person ... private: #pragma db key_type("INT UNSIGNED NOT NULL") - std::map<unsigned short, double> age_weight_map_; + std::map<unsigned short, float> age_weight_map_; ... }; @@ -3867,7 +4214,7 @@ class person ... private: #pragma db key_column("age") - std::map<unsigned short, double> age_weight_map_; + std::map<unsigned short, float> age_weight_map_; ... }; @@ -3892,7 +4239,7 @@ class person ... private: #pragma db value_column("weight") - std::map<unsigned short, double> age_weight_map_; + std::map<unsigned short, float> age_weight_map_; ... }; -- cgit v1.1