ValueTypes值类型
7 值类型
在第3.1节“概念和术语”中,我们已经讨论了值和值类型的概念以及简单值和复合值之间的区别。本章将更详细地介绍简单值和复合值类型。
7.1 Simple Value Types 简单值类型
A simple value type is a fundamental C++ type or a class type that is mapped to a single database column. For each supported database system the ODB compiler provides a default mapping to suitable database types for most fundamental C++ types, such as int or float as well as some class types, such as std::string. For more information about the default mapping for each database system refer to Part II, Database Systems. We can also provide a custom mapping for these or our own value types using the db type pragma (Section 14.3.1, "type").
一个简单的值类型是一个基本的C++类型或映射到单个数据库列的类类型。对于每个支持的数据库系统,ODB编译器为最基本的C++类型提供了默认映射到合适的数据库类型,例如int或浮点以及一些类类型,例如std::string。有关每个数据库系统的默认映射的更多信息,请参阅第二部分“数据库系统”。我们还可以使用db type pragma(第14.3.1节“类型”)为这些或我们自己的值类型提供自定义映射。
7.2 Composite Value Types 复合值类型
A 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:
复合值类型是映射到多个数据库列的类或结构类型。要声明复合值类型,我们使用db value pragma,例如:
#pragma db value
class basic_name
{
...
std::string first_;
std::string last_;
};
The complete version of the above code fragment and the other code samples presented in this section can be found in the composite example in the odb-examples package.
上述代码片段的完整版本和本节中介绍的其他代码示例可以在odb-examples包中的composite 示例中找到。
A composite value type does not have to define a default constructor, unless it is used as an element of a container. In this case the default constructor can be made private provided we also make the odb::access class, defined in the <odb/core.hxx> header, a friend of this value type. For example:
复合值类型不必定义默认构造函数,除非它用作容器的元素。在这种情况下,可以将默认构造函数设置为私有,前提是我们还设置了在<odb/core.hxx>头中定义的odb::access类为此值类型的友元类。例如:
#include <odb/core.hxx>
#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.
...
};
The ODB compiler also needs access to the non-transient (Section 14.4.11, "transient") data members of a composite value type. It uses the same mechanisms as for persistent classes which are discussed in Section 3.2, "Declaring Persistent Objects and Values".
ODB编译器还需要访问复合值类型的非瞬态(第14.4.11节,“瞬态”)数据成员。它使用与第3.2节“声明持久对象和值”中讨论的持久类相同的机制。
The members of a composite value can be other value types (either simple or composite), containers (Chapter 5, "Containers"), and pointers to objects (Chapter 6, "Relationships"). Similarly, a composite value type can be used in object members, as an element of 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 (Section 5.2, "Set and Multiset Containers") and as key types in map containers (Section 5.3, "Map and Multimap Containers"). 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:
复合值的成员可以是其他值类型(简单或复合)、容器(第5章“容器”)和指向对象的指针(第6章“关系”)。类似地,复合值类型可以在对象成员中用作容器的元素,也可以用作另一种复合值类型的基础。特别是,复合值类型可以用作集合容器(第5.2节“集合和多集合容器”)中的元素类型,也可以用作映射容器(第5.3节“映射和多映射容器”)中的键类型。用作容器元素的复合值类型不能包含其他容器,因为不允许容器的容器。以下示例说明了一些可能的用例:
#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
{
...
name name_;
};
A composite value type can be defined inside a persistent class, view, or another composite value and even made private, provided we make odb::access a friend of the containing class, for example:
复合值类型可以在持久类、视图或其他复合值中定义,甚至可以设置为私有,只要我们使odb::access成为包含类的友元类,例如:
#pragma db object
class person
{
...
#pragma db value
class name
{
...
std::string first_;
std::string last_;
};
name name_;
};
A composite value type can also be defined as an instantiation of a C++ class template, for example:
复合值类型也可以定义为C++类模板的实例化,例如:
template <typename T>
struct point
{
T x;
T y;
T z;
};
typedef point<int> int_point;
#pragma db value(int_point)
#pragma db object
class object
{
...
int_point center_;
};
Note that the database support code for such a composite value type is generated when compiling the header containing the db value pragma and not the header containing the template definition or the typedef name. This allows us to use templates defined in other files, such as std::pair defined in the utility standard header file:
请注意,这种复合值类型的数据库支持代码是在编译包含db value pragma的标头时生成的,而不是在编译包含模板定义或typedef名称的标头时生成的。这允许我们使用在其他文件中定义的模板,例如在实用程序标准头文件中定义的std::pair
#include <utility> // std::pair
typedef std::pair<std::string, std::string> phone_numbers;
#pragma db value(phone_numbers)
#pragma db object
class person
{
...
phone_numbers phone_;
};
We can also use data members from 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 member that contains members corresponding to the data members in the value type. We can then use the member access syntax (.) to refer to data members in value types. For example, the query class for the person object presented above contains the name member (its name is derived from the name_ data member) which in turn contains the extras member (its name is derived from the name::extras_ data member of the composite value type). This process continues recursively 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:
我们还可以在数据库查询中使用来自复合值类型的数据成员(第4章,“查询数据库”)。对于持久类中的每个复合值,查询类定义了一个嵌套成员,其中包含与值类型中的数据成员相对应的成员。然后我们可以使用成员访问语法(.)引用值类型中的数据成员。例如,上面显示的person对象的查询类包含name成员(其名称派生自name_数据成员),而name成员又包含extras成员(其名称派生自复合值类型的name::xtras_数据成员)。对于嵌套的复合值类型,此过程将以递归方式继续,因此,我们可以使用query::name.extras.nickname查询数据库中的person对象时的昵称表达式。例如:
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 ();
7.2.1 Composite Object Ids 复合对象Ids
An object id can be of a composite value type, for example:
对象id可以是复合值类型,例如:
#pragma db value
class name
{
...
std::string first_;
std::string last_;
};
#pragma db object
class person
{
...
#pragma db id
name name_;
};
However, a value type that can be used as an object id has a number of restrictions. Such a value type cannot have container, object pointer, or read-only data members. It also must be default-constructible, copy-constructible, and copy-assignable. Furthermore, if the persistent class in which this composite value type is used as object id has session support enabled (Chapter 11, "Session"), then it must also implement the less-than comparison operator (operator<).
但是,可以用作对象id的值类型有许多限制。此类值类型不能具有容器、对象指针或只读数据成员。它还必须是默认可构造、可复制可构造和可复制可分配的。此外,如果将此复合值类型用作对象id的持久类启用了会话支持(第11章“会话”),则它还必须实现小于比较运算符(运算符<)。
7.2.2 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 (Section 14.4.9, "column"). For composite value types things are slightly more complex since they are mapped to multiple columns. Consider the following example:
为简单值类型的数据成员自定义列名非常简单,我们只需使用 db column pragma(第14.4.9节“列”)指定所需的名称。对于复合值类型,情况稍微复杂一些,因为它们映射到多个列。考虑下面的例子:
#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:
first_和last_成员的列名是通过使用person::name_成员的净化名称作为前缀,使用值类型(first_和last_)中成员的名称作为后缀来构造的。因此,上述类的数据库架构如下所示:
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:
我们可以使用db column pragma自定义前缀和后缀,如下例所示:
#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 principle 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 (Section 14.4.36, "value_column") or db key_column (Section 14.4.35, "key_column") pragmas are used to specify the column prefix.
当复合值类型用作容器的元素时,同样的原则也适用,除了使用db value_column (第14.4.36节“value_column ”)或db key_column (第14.4.35节“key_column ”)pragmas 来指定列前缀而不是db column。
When a composite value type contains a container, an extra table is used to store its elements (Chapter 5, "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:
当复合值类型包含容器时,将使用一个额外的表来存储其元素(第5章“容器”)。这些表的名称的构造方式与列名类似,只是默认情况下对象名和成员名都用作前缀。例如:
#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 pragma (Section 14.4.20, "table"), for example:
要自定义容器表名称,我们可以使用db table pragma(第14.4.20节,“表”),例如:
#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.
与列类似,我们可以将表前缀设为空。
7.3 Pointers and NULL Value Semantics 指针和空值语义
Relational database systems have a notion of the special NULL value that is used to indicate the absence of a valid value in a column. While by default ODB maps values to columns that do not allow NULL values, it is possible to change that with the db null pragma (Section 14.4.6, "null/not_null").
关系数据库系统有一个特殊空值的概念,用于指示列中缺少有效值。虽然默认情况下ODB将值映射到不允许空值的列,但可以使用db null pragma(第14.4.6节“NULL/not_NULL”)更改该值。
To properly support the NULL semantics, the C++ value type must have a notion of a NULL value or a similar special state concept. Most basic C++ types, such as int or std::string, do not have this notion and therefore cannot be used directly for NULL-enabled data members (in the case of a NULL value being loaded from the database, such data members will be default-initialized).
为了正确地支持空语义,C++值类型必须具有空值或类似的特殊状态概念。大多数基本的C++类型,如int或std::string,都没有这个概念,因此不能直接用于启用NULL的数据成员(在从数据库加载空值的情况下,这样的数据成员将默认初始化)。
To allow the easy conversion of value types that do not support the NULL semantics into the ones that do, ODB provides the odb::nullable class template. It allows us to wrap an existing C++ type into a container-like class that can either be NULL or contain a value of the wrapped type. ODB also automatically enables the NULL values for data members of the odb::nullable type. For example:
为了方便地将不支持NULL语义的值类型转换为支持NULL语义的值类型,ODB提供了ODB::nullable类模板。它允许我们将现有C++类型封装成容器类,它可以是null,也可以包含包装类型的值。ODB还自动为ODB::nullable类型的数据成员启用空值。例如:
#include <odb/nullable.hxx>
#pragma db object
class person
{
...
std::string first_; // TEXT NOT NULL
odb::nullable<std::string> middle_; // TEXT NULL
std::string last_; // TEXT NOT NULL
};
The odb::nullable class template is defined in the <odb/nullable.hxx> header file and has the following interface:
namespace odb
{
template <typename T>
class nullable
{
public:
typedef T value_type;
nullable ();
nullable (const T&);
nullable (const nullable&);
template <typename Y> explicit nullable (const nullable<Y>&);
nullable& operator= (const T&);
nullable& operator= (const nullable&);
template <typename Y> nullable& operator= (const nullable<Y>&);
void swap (nullable&);
// Accessor interface.
//
bool null () const;
T& get ();
const T& get () const;
// Pointer interface.
//
operator bool_convertible () const;
T* operator-> ();
const T* operator-> () const;
T& operator* ();
const T& operator* () const;
// Reset to the NULL state.
//
void reset ();
};
}
The following example shows how we can use this interface:
nullable<string> ns;
// Using the accessor interface.
//
if (ns.null ())
{
s = "abc";
}
else
{
string s (ns.get ());
ns.reset ();
}
// The same using the pointer interface.
//
if (ns)
{
s = "abc";
}
else
{
string s (*ns);
ns.reset ();
}
The odb::nullable class template requires the wrapped type to have public default and copy constructors as well as the copy assignment operator. Note also that the odb::nullable implementation is not the most efficient in that it always contains a fully constructed value of the wrapped type. This is normally not a concern for simple types such as the C++ fundamental types or std::string. However, it may become an issue for more complex types. In such cases you may want to consider using a more efficient implementation of the optional value concept such as the optional class template from Boost (Section 23.4, "Optional Library").
odb::nullable 类模板要求包装类型具有公共默认构造函数和复制构造函数以及复制赋值运算符。还要注意,odb::nullable实现并不是最有效的,因为它总是包含一个完全构造的包装类型的值。这通常不是简单类型的问题,例如C++基本类型或std::String。但是,对于更复杂的类型,这可能会成为一个问题。在这种情况下,您可能需要考虑使用更有效的实现可选值的概念,如Boost中的可选类模板(第23.4节,“可选库”)。
Another common C++ representation of a value that can be NULL is a pointer. ODB will automatically handle data members that are pointers to values, however, it will not automatically enable NULL values for such data members, as is the case for odb::nullable. Instead, if the NULL value is desired, we will need to enable it explicitly using the db null pragma. For example:
另一个可以为NULL的值的另一种常见的C++表示是指针。ODB将自动处理作为值指针的数据成员,但是,它不会像ODB::nullable那样自动为这些数据成员启用空值。相反,如果需要NULL值,我们需要使用db null pragma显式地启用它。例如:
#pragma db object
class person
{
...
std::string first_;
#pragma db null
std::auto_ptr<std::string> middle_;
std::string last_;
};
The ODB compiler includes built-in support for using std::auto_ptr, std::unique_ptr (C++11), and shared_ptr (TR1 or C++11) as pointers to values. Plus, ODB profile 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").
ODB编译器内置支持使用std::auto_ptr、std::unique_ptr(C++11)和shared_ptr(TR1或C++11)作为值指针。另外,ODB概要文件库可用于常用的框架和库(如Boost和Qt),为这些框架和库中的智能指针提供支持(第三部分,“概要文件”)。
ODB also supports the NULL semantics for composite values. In the relational database the NULL composite value is translated to NULL values for all the simple data members of this composite value. For example:
ODB还支持复合值的NULL语义。在关系数据库中,对于该复合值的所有简单数据成员,将NULL复合值转换为NULL值。例如:
#pragma db value
struct name
{
std::string first_;
odb::nullable<std::string> middle_;
std::string last_;
};
#pragma db object
class person
{
...
odb::nullable<name> name_;
};
ODB不支持容器的空语义。这也意味着包含容器的复合值不能为NULL。考虑到这个限制,我们仍然可以在容器类型的数据成员中使用智能指针。唯一的限制是这些指针不能为NULL。例如:
#pragma db object
class person
{
...
std::auto_ptr<std::vector<std::string> > aliases_;
};