116 lines
4.1 KiB
C++
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;
|
||
|
}
|