ODB 1.1.0 was released today. In case you are not familiar with ODB, it is an object-relational mapping (ORM) system for C++. It allows you to persist C++ objects to a relational database without having to deal with tables, columns, or SQL, or manually writing any of the mapping code.

This version took a bit longer than we initially planned, mainly because many of the new features depended on each other and, once we implemented one, then it also made sense to implement the others. For example, bidirectional object relationships (see below) are pretty much useless in any real-world scenario without support for lazy loading.

But, in the end, the result is a version that is packed with major new features, which I am going to examine next.

Containers

It is now possible to store containers in the database. Built-in support is provided for most standard C++ containers, namely, vector , list , map , multimap , set , and multiset . For example, now you can write:

#pragma db object class person { ... std::set<std::string> emails_; };

And that’s it. That’s all you need to do. It is also very easy to add support for custom container types. All you need to do is provide a traits specialization for your container (see the ODB manual for details). Plus we are working on profile libraries that will provide support for containers found in popular framework and libraries, such as Boost and Qt.

Relationships

ODB now supports unidirectional (to-one, to-many) and bidirectional (one-to-one, one-to-many, many-to-many) object relationships. For example:

#pragma db object class employer { ... #pragma db inverse(employer_) std::vector<lazy_weak_ptr<employee> > employees_; }; #pragma db object class employee { ... shared_ptr<employer> employer_; };

In the above code fragment we used lazy_weak_ptr , which is a lazy pointer. The object that it points to is only loaded when requested.

In bidirectional relationships it is also possible to declare one side inverse, as shown in the code above. This results in the canonical relational model where only one table contains a reference to the other table.

Similar to containers, ODB provides built-in support (and the corresponding lazy pointers) for standard C++ pointers, namely, the raw pointer, auto_ptr , and TR1 shared_ptr / lazy_ptr . At the same time, profile libraries will provide support for smart pointers found in popular framework and libraries, such as Boost shared_ptr and Qt QSharedPointer . It is also easy to add support for your own smart pointers, as described in the ODB manual.

Composite Values

ODB prior to 1.1.0 only supported simple (single-column) value types. Now you can declare and use composite (multi-column) value types, for example:

#pragma db value class name { ... std::string first_; std::string last_; }; #pragma db object class person { ... name name_; };

Of course, all these features work well with each other. For example, you can have a container of object pointer or composite value types. Similarly, a composite value type can have members that are other composite value types, object pointers, or containers. Plus, these features are integrated into the query language so that you can use data members from composite values or related objects in query expressions, for example:

typedef odb::query<person> person_query; typedef odb::query<employee> employee_query; db.query<person> (person_query::name::last == "Doe"); db.query<employee> (employee_query::employer::name == "Example Inc");

There are also other important features in this release, including, optional object cache (session), customizable object pointers, native SQL statement execution, etc. For an exhaustive list of new features see the official ODB 1.1.0 release announcement.