discord-bot/libs/sqlite_orm-1.8.2/examples/subentities.cpp

116 lines
4.1 KiB
C++

/**
* Our goal is to manage a Student class. Student has basic id, name and roll_number and one more thing:
* vector of marks. Mark is a subentity with one to many relation. This example shows how to manage this kind of case.
* First of all we got to understand how to keep data in the db. We need two tables: `students` and `marks`. Students
* table has column equal to all Student class members exept marks. Marks table has two columns: student_id and value
* itself. We create two functions here: inserting/updating student (with his/her marks) and getting student (also with
* his/her marks). Schema is: `CREATE TABLE students (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, roll_no
* INTEGER NOT NULL)` `CREATE TABLE marks (mark INTEGER NOT NULL, student_id INTEGER NOT NULL)` One of the main ideas of
* `sqlite_orm` is to give a developer ability to name tables/columns as he/she wants. Many other ORM libraries manage
* subentities automatically and it is not correct cause developer must understand how everything works inside sqlite
* otherwise his/her app might not work properly. Also developer must know schema in case he or she needs a strict
* access with sqlite client.
*/
#include <sqlite_orm/sqlite_orm.h>
#include <iostream>
using std::cout;
using std::endl;
class Mark {
public:
int value;
int student_id;
};
class Student {
public:
int id;
std::string name;
int roll_number;
std::vector<decltype(Mark::value)> marks;
};
using namespace sqlite_orm;
auto storage =
make_storage("subentities.sqlite",
make_table("students",
make_column("id", &Student::id, primary_key()),
make_column("name", &Student::name),
make_column("roll_no", &Student::roll_number)),
make_table("marks", make_column("mark", &Mark::value), make_column("student_id", &Mark::student_id)));
// inserts or updates student and does the same with marks
int addStudent(const Student& student) {
auto studentId = student.id;
if(storage.count<Student>(where(c(&Student::id) == student.id))) {
storage.update(student);
} else {
studentId = storage.insert(student);
}
// insert all marks within a transaction
storage.transaction([&] {
storage.remove_all<Mark>(where(c(&Mark::student_id) == studentId));
for(auto& mark: student.marks) {
storage.insert(Mark{mark, studentId});
}
return true;
});
return studentId;
}
/**
* To get student from db we have to execute two queries:
* `SELECT * FROM students WHERE id = ?`
* `SELECT mark FROM marks WHERE student_id = ?`
*/
Student getStudent(int studentId) {
auto res = storage.get<Student>(studentId);
res.marks = storage.select(&Mark::value, where(c(&Mark::student_id) == studentId));
return res; // must be moved automatically by compiler
}
int main(int, char**) {
decltype(Student::id) mikeId;
decltype(Student::id) annaId;
{
storage.sync_schema(); // create tables if they don't exist
Student mike{-1, "Mike", 123, {}}; // create student named `Mike` without marks and without id
mike.marks = {3, 4, 5};
mike.id = addStudent(mike);
mikeId = mike.id;
// also let's create another students with marks..
Student anna{-1, "Anna", 555, {}};
anna.marks.push_back(6);
anna.marks.push_back(7);
anna.id = addStudent(anna);
annaId = anna.id;
}
// now let's assume we forgot about object `mike`, let's try to get him with his marks
// assume we know `mikeId` variable only
{
auto mike = getStudent(mikeId);
cout << "mike = " << storage.dump(mike) << endl;
cout << "mike.marks = ";
for(auto& m: mike.marks) {
cout << m << " ";
}
cout << endl;
auto anna = getStudent(annaId);
cout << "anna = " << storage.dump(anna) << endl;
cout << "anna.marks = ";
for(auto& m: anna.marks) {
cout << m << " ";
}
cout << endl;
}
return 0;
}