From 762448ca92b948c1701d07a3403e93f1c27c19bd Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 12 Jan 2011 11:29:34 +0200 Subject: Add chapter on composite value types, misc fixes --- doc/manual.xhtml | 420 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 384 insertions(+), 36 deletions(-) diff --git a/doc/manual.xhtml b/doc/manual.xhtml index 396c0ea..bdfb6fd 100644 --- a/doc/manual.xhtml +++ b/doc/manual.xhtml @@ -1539,13 +1539,18 @@ Hello, Joe! and an instance of a value type — a value.

An object is an independent entity. It can be stored, updated, - and deleted in the database independent of other objects or values. + and deleted in the database independent of other objects. An object has an identifier, called object id, that is - unique among all instances of an object type within a database. - An object consists of data members which are either values or - references to other objects. In contrast, a value can only be - stored in the database as part of an object and doesn't have - its own unique identifier.

+ unique among all instances of an object type within a database. In + contrast, a value can only be stored in the database as part of an + object and doesn't have its own unique identifier.

+ +

An object consists of data members which are either values, pointers + to other objects (Chapter Y, "Relationships"), or + containers of values or pointers to other objects (Chapter + X, "Containers"). Pointers to other objects and containers can + be viewed as special kinds of values since they also can only + be stored in the database as part of an object.

An object type is a C++ class. Because of this one-to-one relationship, we will use terms object type @@ -1566,7 +1571,7 @@ Hello, Joe! viewed (and mapped) as both simple and composite by different applications.

-

Seeing how all these concepts map to the relational model +

Understanding how all these concepts map to the relational model will hopefully make these distinctions clearer. In a relational database an object type is mapped to a table and a value type is mapped to one or more columns. A simple value type is mapped @@ -1600,11 +1605,11 @@ Hello, Joe! example:

-    #pragma db object
-    class person
-    {
-      ...
-    };
+#pragma db object
+class person
+{
+  ...
+};
   

The other pragma that we often use is db id @@ -1612,14 +1617,15 @@ Hello, Joe! example:

-    #pragma db object
-    class person
-    {
+#pragma db object
+class person
+{
+  ...
 
-    private:
-      #pragma db id
-      unsigned long id_;
-    };
+private:
+  #pragma db id
+  unsigned long id_;
+};
   

These two pragmas are the minimum required to declare a @@ -1634,10 +1640,26 @@ Hello, Joe! how to convert between the two. On the other hand, if a simple value is unknown to the ODB compiler then you will need to provide the mapping to the database system type and, possibly, the code to - convert between the two. For more information on this refer to - Section 5.3, "Value Type Pragmas". Composite - value types are not yet supported by ODB and we will not discuss - them further in this revision of the manual.

+ convert between the two. For more information on how to achieve + this refer to the db type pragma (Section + 5.3.1, "type"). Similar to object types, composite + value types have to be explicitly declared as persistent using the + db value pragma, for example:

+ +
+#pragma db value
+class name
+{
+  ...
+
+private:
+  std::string first_;
+  std::string last_;
+};
+  
+ +

Composite values are discussed in greater detail in Chapter + Z, "Composite Value Types".

Normally, you would use object types to model real-world entities, things that have their own identity. For example, in the @@ -1698,15 +1720,15 @@ Hello, Joe! database system:

-  #include <odb/database.hxx>
-  #include <odb/mysql/database.hxx>
+#include <odb/database.hxx>
+#include <odb/mysql/database.hxx>
 
-  auto_ptr<odb::database> db (
-    new odb::mysql::database (
-      "test_user"     // database login name
-      "test_password" // database password
-      "test_database" // database name
-      ));
+auto_ptr<odb::database> db (
+  new odb::mysql::database (
+    "test_user"     // database login name
+    "test_password" // database password
+    "test_database" // database name
+    ));
   

The odb::database class is a common interface for @@ -2324,11 +2346,11 @@ private: 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.

+ types (see @@), and pointers to objects (see @@). Containers of + containers, either directly or indirectly via a composite value + type, are not allowed. A key in map and multimap containers can + be a simple or composite value type but not a pointer to an object. + 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. @@ -3626,6 +3648,322 @@ t.commit (); +

Z Composite Value Types

+ +

Composite value type is a class or struct + type that is mapped to more than one database column. To declare + a composite value type we use the db value pragma, + for example:

+ +
+#pragma db value
+class basic_name
+{
+  ...
+
+  std::string first_;
+  std::string last_;
+};
+  
+ +

A composite value type does not have to define a default constructor, + unless it is used as an element of a container, in which case the + default constructor can be made private. If a composite value type + has private or protected non-transient data members or if its + default constructor is not public and the value type is used as + an element in a container, then the odb::access class + should be declared a friend of this value type. For example:

+ +
+#pragma db value
+class basic_name
+{
+public:
+  basic_name (const std::string& first, const std::string& last);
+
+  ...
+
+private:
+  friend class odb::access;
+
+  basic_name () {} // Needed for storing basic_name in containers.
+
+  std::string first_;
+  std::string last_;
+};
+  
+ +

The complete versions of the above code fragment as well as other code + samples presented in this chapter can found in the composite + example in the odb-examples package.

+ +

The members of a composite value can be other value types (either + simple or composite), containers (@@ ref), and object pointers (@@ ref). + Similarly, a composite value type can be used in object members, + as an element in a container, and as a base for another composite + value type. In particular, composite value types can be used as + element types in set containers (@@ ref) and as key types in map + containers (@@ ref). A composite value type that is used as an + element of a container cannot contain other containers since + containers of containers are not allowed. The following example + illustrates some of the possible use cases:

+ +
+#pragma db value
+class basic_name
+{
+  ...
+
+  std::string first_;
+  std::string last_;
+};
+
+typedef std::vector<basic_name> basic_names;
+
+#pragma db value
+class name_extras
+{
+  ...
+
+  std::string nickname_;
+  basic_names aliases_;
+};
+
+#pragma db value
+class name: public basic_name
+{
+  ...
+
+  std::string title_;
+  name_extras extras_;
+};
+
+#pragma db object
+class person
+{
+  ...
+
+  #pragma db id auto
+  unsigned long id_;
+
+  name name_;
+};
+
+ +

We can also use the data members from the composite value types + in database queries (Chapter 4, "Querying the + Database"). For each composite value in a persistent class, + the query class defines a nested scope containing members corresponding + to the data members in the value type. For example, the + query class for the person object presented above + contains the name scope (derived from the name_ + data member) which in turn contains the extras member + (derived from the name::extras_ data member of the + composite value type). The process continues reqursively for nested + composite value types and, as a result, we can use the + query::name::extras::nickname expression while querying + the database for the person objects. For example:

+ +
+typedef odb::query<person> query;
+typedef odb::result<person> result;
+
+transaction t (db->begin ());
+
+result r (db->query<person> (
+  query::name::extras::nickname == "Squeaky"));
+
+...
+
+t.commit ();
+  
+ +

Z.1 Composite Value Column and Table Names

+ +

Customizing a column name for a data member of a simple value + type is straightforward: we simply specify the desired name with + the db column pragma (@@ ref). For composite value + types things are slightly more complicated since it is mapped to + multiple columns. Consider the following example:

+ +
+#pragma db value
+class name
+{
+  ...
+
+  std::string first_;
+  std::string last_;
+};
+
+#pragma db object
+class person
+{
+  ...
+
+  #pragma db id auto
+  unsigned long id_;
+
+  name name_;
+};
+
+ +

The column names for the first_ and last_ + members are constructed by using the sanitized name of the + person::name_ member as a prefix and the names of the + members in the value type (first_ and last_) + as suffixes. As a result, the database schema for the above classes + will look like this:

+ +
+CREATE TABLE person (
+  id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+  name_first TEXT NOT NULL,
+  name_last TEXT NOT NULL);
+  
+ +

We can customize both the prefix and the suffix using the + db column pragma as shown in the following + example:

+ +
+#pragma db value
+class name
+{
+  ...
+
+  #pragma db column("first_name")
+  std::string first_;
+
+  #pragma db column("last_name")
+  std::string last_;
+};
+
+#pragma db object
+class person
+{
+  ...
+
+  #pragma db column("person_")
+  name name_;
+};
+  
+ +

The database schema changes as follows:

+ +
+CREATE TABLE person (
+  id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+  person_first_name TEXT NOT NULL,
+  person_last_name TEXT NOT NULL);
+  
+ +

We can also make the column prefix empty, for example:

+ +
+#pragma db object
+class person
+{
+  ...
+
+  #pragma db column("")
+  name name_;
+};
+  
+ +

This will result in the following schema:

+ +
+CREATE TABLE person (
+  id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+  first_name TEXT NOT NULL,
+  last_name TEXT NOT NULL);
+  
+ +

The same principal applies when a composite value type is used + as an element of a container, except that instead of + db column either the db value_column + (@@ ref) or db key_column (@@ ref) pragmas are used to + specify the column prefix.

+ +

When a composite value type contains a container, an extra table + is used to store the elements (@@ ref Chapter X, "Containers"). + The names of such tables are constructed in a way similar to the + 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
+{
+  ...
+
+  std::string first_;
+  std::string last_;
+  std::vector<std::string> nicknames_;
+};
+
+#pragma db object
+class person
+{
+  ...
+
+  name name_;
+};
+  
+ +

The corresponding database schema will look like this:

+ +
+CREATE TABLE `person_name_nicknames` (
+  `object_id` BIGINT UNSIGNED NOT NULL,
+  `index` BIGINT UNSIGNED NOT NULL,
+  `value` TEXT NOT NULL)
+
+CREATE TABLE person (
+  id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
+  name_first TEXT NOT NULL,
+  name_last TEXT NOT NULL);
+  
+ +

To customize the container table name we can use the + db table (@@ ref) pragma, for example:

+ +
+#pragma db value
+class name
+{
+  ...
+
+  #pragma db table("nickname")
+  std::vector<std::string> nicknames_;
+};
+
+#pragma db object
+class person
+{
+  ...
+
+  #pragma db table("person_")
+  name name_;
+};
+  
+ +

This will result in the following schema changes:

+ +
+CREATE TABLE `person_nickname` (
+  `object_id` BIGINT UNSIGNED NOT NULL,
+  `index` BIGINT UNSIGNED NOT NULL,
+  `value` TEXT NOT NULL)
+  
+ +

Similar to columns, we can make the table prefix empty.

+ + + + +

4 Querying the Database

If you don't know the identifiers of the objects that you are looking @@ -5009,6 +5347,10 @@ private: }; +

For a member of a composite value type, the column specifier + specifies the column name prefix. Refer to Section Z.1, + "Composite Value Column and Table Names" for details.

+

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.

@@ -5152,6 +5494,12 @@ private: above, without the table specifier, the container's table name would have been person_nicknames.

+

The table specifier can also be used for members of + composite value types. In this case it specifies the table name + prefix for container members inside the composite value type. Refer + to Section Z.1, "Composite Value Column and Table + Names" for details.

+

5.4.10 index_type

The index_type specifier specifies the native -- cgit v1.1