ODB 1.3.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.
As usual, for the complete list of changes see the official ODB 1.3.0 announcement. But the most important new feature in this release is no doubt support for the SQLite embedded database. Below I am going to examine this in more detail.
Support for SQLite is provided by the
libodb-sqlite runtime library. Pretty much all the standard ODB functionality is available to you when using SQLite, including support for containers, object relationships, queries, date-time types in the Boost profile, etc. There are a few limitations, however, that are discussed in Chapter 11, “SQLite Database” in the ODB Manual.
If you request the generation of the database schema (
--generate-schema ODB compiler option), then for SQLite, by default, the schema is embedded into the generated C++ code since most applications that use SQLite probably don’t want to carry a
.sql file around. See the
schema/embedded example in the
odb-examples package for more information on how to use embedded schemas.
If you have used SQLite before, you are probably aware of the peculiar ways in which it manages access to the same database from multiple threads and processes. While more traditional database systems will make concurrent access pretty much transparent to the user (except for an occasional deadlock or timeout), SQLite simply returns an error if the database is used by someone else. There are some advanced mechanisms available, such as the shared cache mode and unlock notifications, which allow the user to implement more traditional (i.e., blocking) concurrent access to the database from within a multi-threaded program. But it is still quite a lot of low-level, non-trivial code that one has to write.
The good news is that ODB takes care of all these details and allows you to access the same database from multiple threads in the same way as you would with any other database system. For connection management, ODB provides three built-in connection factories (you can also provide your own if so desired):
The single connection factory shares a single connection among all the callers. So if one thread is using the connection, all the others requesting a connection will be blocked until it is done.
The new connection factory creates a new connection whenever one is requested. Once the connection is no longer needed, it is closed.
The connection pool factory maintains a pool of connections and you can specify the
max connection counts for each pool created. This factory is the default choice when creating a database instance.
The new and pool factories are the best options for multi-threaded applications. By default they enable the SQLite shared cache mode and use the unlock notifications to aid concurrency.
Another SQLite-specific feature worth mentioning is support for starting immediate and exclusive SQLite transactions. This is accomplished with two additional
begin_exclusive(). These functions are primarily useful for avoiding deadlocks.
If you would like to learn more about SQLite support in ODB, the best place to start is Chapter 11, “SQLite Database” in the ODB Manual.