/** * 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 #include 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 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(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(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(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; }