Appearance
ORM
This page is a guide to the Vix ORM module with minimal, explicit examples.
Assumptions:
- MySQL is running locally
- Database:
vixdb - Table
users(id, name, email, age)exists
Run any example with:
bash
vix run examples/orm/<file>.cppConcepts
Vix ORM is built on top of the vix::db layer and keeps everything explicit:
- Connection pools
- Transactions (RAII)
- Prepared statements
- Small helpers for mapping rows to C++ types
Nothing is hidden. SQL stays visible.
1) Connection pool + basic insert (transaction)
cpp
#include <vix/orm/orm.hpp>
#include <iostream>
using namespace vix::orm;
int main()
{
auto factory = make_mysql_factory(
"tcp://127.0.0.1:3306",
"root",
"",
"vixdb"
);
PoolConfig cfg;
cfg.min = 1;
cfg.max = 8;
ConnectionPool pool{factory, cfg};
pool.warmup();
try
{
Transaction tx(pool);
auto &c = tx.conn();
auto st = c.prepare("INSERT INTO users(name,email,age) VALUES(?,?,?)");
st->bind(1, "Alice");
st->bind(2, "alice@example.com");
st->bind(3, 25);
st->exec();
tx.commit();
std::cout << "Inserted\n";
return 0;
}
catch (const std::exception &e)
{
std::cerr << e.what() << "\n";
return 1;
}
}Notes:
Transactionrolls back automatically if you do not callcommit().- Binding is positional and 1-based (1, 2, 3...).
2) QueryBuilder UPDATE
Use QueryBuilder when you want to build SQL with parameters, while keeping parameters separate from the SQL string.
cpp
#include <vix/orm/orm.hpp>
#include <iostream>
using namespace vix::orm;
int main()
{
auto factory = make_mysql_factory(
"tcp://127.0.0.1:3306",
"root",
"",
"vixdb"
);
ConnectionPool pool{factory, {1, 8}};
pool.warmup();
try
{
QueryBuilder qb;
qb.raw("UPDATE users SET age=? WHERE email=?")
.param(29)
.param(std::string("alice@example.com"));
PooledConn pc(pool);
auto st = pc.get().prepare(qb.sql());
const auto &ps = qb.params();
for (std::size_t i = 0; i < ps.size(); ++i)
st->bind(i + 1, ps[i]);
std::cout << "Affected rows: " << st->exec() << "\n";
return 0;
}
catch (const std::exception &e)
{
std::cerr << e.what() << "\n";
return 1;
}
}3) Repository CRUD
To use the repository, define:
- Your entity struct
- A
Mapper<T>specialization (row to entity, entity to params)
cpp
#include <vix/orm/orm.hpp>
#include <iostream>
#include <string>
struct User
{
std::int64_t id{};
std::string name;
std::string email;
int age{};
};
namespace vix::orm
{
template <>
struct Mapper<User>
{
static User fromRow(const ResultRow &row)
{
User u{};
u.id = row.getInt64Or(0, 0);
u.name = row.getStringOr(1, "");
u.email = row.getStringOr(2, "");
u.age = static_cast<int>(row.getInt64Or(3, 0));
return u;
}
static std::vector<std::pair<std::string, std::any>>
toInsertParams(const User &u)
{
return {
{"name", u.name},
{"email", u.email},
{"age", u.age},
};
}
static std::vector<std::pair<std::string, std::any>>
toUpdateParams(const User &u)
{
return {
{"name", u.name},
{"email", u.email},
{"age", u.age},
};
}
};
}
int main()
{
using namespace vix::orm;
auto factory = make_mysql_factory(
"tcp://127.0.0.1:3306",
"root",
"",
"vixdb"
);
ConnectionPool pool{factory, {1, 8}};
pool.warmup();
BaseRepository<User> repo{pool, "users"};
try
{
auto id = repo.create(User{0, "Bob", "bob@example.com", 30});
std::cout << "Created id=" << id << "\n";
repo.updateById(static_cast<std::int64_t>(id),
User{static_cast<std::int64_t>(id), "Bobby", "bob@example.com", 31});
if (auto u = repo.findById(static_cast<std::int64_t>(id)))
std::cout << "Found: " << u->name << "\n";
repo.removeById(static_cast<std::int64_t>(id));
std::cout << "Deleted\n";
return 0;
}
catch (const std::exception &e)
{
std::cerr << e.what() << "\n";
return 1;
}
}4) UnitOfWork
When you want a short transactional scope without writing SQL in every place, use UnitOfWork.
cpp
#include <vix/orm/orm.hpp>
#include <iostream>
using namespace vix::orm;
int main()
{
auto factory = make_mysql_factory(
"tcp://127.0.0.1:3306",
"root",
"",
"vixdb"
);
ConnectionPool pool{factory, {1, 8}};
pool.warmup();
try
{
UnitOfWork uow{pool};
auto &c = uow.conn();
c.prepare("INSERT INTO users(name,email,age) VALUES(?,?,?)")
->bind(1, "Charlie")
->bind(2, "charlie@example.com")
->bind(3, 22)
->exec();
uow.commit();
std::cout << "Committed\n";
return 0;
}
catch (const std::exception &e)
{
std::cerr << e.what() << "\n";
return 1;
}
}Common errors
- Cannot connect: verify host, port, user, password, and database name
- Missing table: create the table or run migrations first
- Bind mismatch: bind integers as
std::int64_twhen in doubt
Vix ORM stays minimal: explicit SQL, predictable transactions, clear failures.