C++ ORM for SQLite
C++ object-relational mapping with API inspired by the awesome Boost.Serialization - that means almost no API to learn.
Just compile and link all files under src/ to your project. Include the main header:
#include "hiberlite.h"Key features of Hiberlite are:
In contrast to most serialization libraries with SQL serializers, C++ objects mapped with hiberlite behave similar to active record pattern - you are not forced to follow the "read all your data/modify some small part/write everything back" path.
For people who need reliable data storage, ACID transactions, simple random-access to their data files, and don't like coding in SQL.
While building a typical RDBMS application developer has to deal with the following tasks:
Hiberlite greatly reduces the implementation of each of these tasks.
Opening a database is as simple as
hiberlite::Database db;
db.open("sample.db");Where
Database::open(std::string filename)opens a sqlite3 file. If it doesn't exist it will be created. Another option is to have constructor automatically open/create the DB file:
hiberlite::Database db("sample.db");In C++ You deal with classes and objects. You know what objects You want to store in the database. And hiberlite will figure out how to store that data.
First You must prepare the data classes for use with hiberlite. Suppose we develop a social-network application. We define a person class as:
class Person{
public:
string name;
int age;
vector bio;
};Now, to let hiberlite know about the internal structure of this class, we add an access method and an export declaration:
class Person{
friend class hiberlite::access;
template
void hibernate(Archive & ar)
{
ar & HIBERLITE_NVP(name);
ar & HIBERLITE_NVP(age);
ar & HIBERLITE_NVP(bio);
}
public:
string name;
double age;
vector bio;
};
HIBERLITE_EXPORT_CLASS(Person)Inside the hibernate(Archive & ar) method we list all the data fields we want to persist in the database. Macro HIBERLITE_EXPORT_CLASS(Person) is invoked to register the class name with Hiberlite. For now just remember to invoke it once per class in your project. (Placing it in the Person.cpp is a good idea)
At this point hiberlite is able to map the Person to a database.
Database schema in hiberlite is simply a set of classes, that are stored in the database. A programmer defines that set of classes, and hiberlite determines the needed tables and their columns.
Each instance of Database maintains its own copy of the database schema (set of data classes). To insert a new class to that set, use registerBeanClass template method:
db.registerBeanClass();In most applications several classes are stored in one database - so usually You will call registerBeanClass several times:
Database db("gmail2.0.db");
db.registerBeanClass();
db.registerBeanClass();
db.registerBeanClass();When the classes are registered, Database can create tables to map them:
db.dropModel();
db.createModel();createModel() executes several CREATE TABLE queries. dropModel() cleans the database - executes DROP TABLE IF EXISTS query for each table in the schema. Besides uninstallers, this method may be used to destroy the old tables (from a probably obsolete scheme) before calling createModel().
When the database with proper schema is opened, we can put some Person objects in it:
Person x;
x.name="Amanda";
x.age=21;
x.bio.push_back("born 1987");
hiberlite::bean_ptr p=db.copyBean(x);
x.age=-1; //does NOT change any database record
p->age=22; //does change the databasecopyBean(x) template method creates a copy of its argument and saves it to the database. It returns a smart pointer - bean_ptr.
bean_ptr is inspired by the boost::shared_ptr. The main difference is that in addition to deletion, bean_ptr guarantees that the referenced object will be also saved to the database, when no longer needed.
An internal primary key is autogenerated for each object in the database. It can be read with sqlid_t bean_ptr::get_id().
Another way to create a bean is to use createBean() template method:
hiberlite::bean_ptr p=db.createBean();
p->name="Amanda";
p->age=21;
p->bio.push_back("born 1987");There are several methods to query the database:
bean_ptr Database::loadBean(sqlid_t id) loads the bean with the given id.
bean_ptr p=db.loadBean(1);
cout name > Database::getAllBeans()**
returns a vector with all the beans of a given class C.
```cpp
vector v=db.getAllBeans();
cout
void hibernate(Archive & ar)
{
ar & HIBERLITE_NVP(a);
ar & HIBERLITE_NVP(b);
ar & HIBERLITE_NVP(vs);
}
public:
int a;
double b;
vector vs;
};
HIBERLITE_EXPORT_CLASS(MyClass)Note the friend declaration and the hibernate(...) template method - these two pieces of code are necessary for hiberlite to access the internal structure of the user-defined class.
HIBERLITE_NVP is a macro that creates a name-value pair with reference to its argument.
HIBERLITE_EXPORT_CLASS() defines the root table name for the class. More on this later.
hiberlite will use 2 tables to represent MyClass instances in the database:
CREATE TABLE MyClass
(
a INTEGER,
b REAL,
hiberlite_id INTEGER PRIMARY KEY AUTOINCREMENT
)
CREATE TABLE MyClass_vs_items
(
hiberlite_entry_indx INTEGER,
hiberlite_id INTEGER PRIMARY KEY AUTOINCREMENT,
hiberlite_parent_id INTEGER,
item TEXT
)
…
cpp
hiberlite::bean_ptr p=db.copyBean(x);
…
cpp
std::vector > v=db.getAllBeans();
…
cpp
enum class Status {
OK,
NOT_OK
};the macro HIBERLITE_NVP will cause a compile error if a member of that type is used with it. In order to convert the enum to a number and back (conversion safety considerations may apply), it is possible to define custom conversions before defining the data model:
namespace hiberlite {
template
void hibernate(A& ar, Status& value, const unsigned int) {
ar& db_atom(value);
}
template <>
inline std::string db_atom::sqliteStorageClass() {
return "INTEGER";
}
template <>
template
void db_atom::loadValue(Stmt& res, Arg& arg) {
val = static_cast(res.get_int(arg));
}
template <>
inline void db_atom::bindValue(sqlite3_stmt* stmt, int col) {
sqlite3_bind_int(stmt, col, static_cast(val));
}
}This allows the following to work:
struct Item {
Status status;
template
void hibernate(Archive & ar) {
ar & HIBERLITE_NVP(status);
}
};No open issues yet, or sync has not completed.