diff options
Diffstat (limited to 'odb-examples/qt')
-rw-r--r-- | odb-examples/qt/README | 81 | ||||
-rw-r--r-- | odb-examples/qt/buildfile | 55 | ||||
-rw-r--r-- | odb-examples/qt/database.hxx | 95 | ||||
-rw-r--r-- | odb-examples/qt/driver.cxx | 180 | ||||
-rw-r--r-- | odb-examples/qt/employee.hxx | 167 | ||||
-rw-r--r-- | odb-examples/qt/testscript | 13 |
6 files changed, 591 insertions, 0 deletions
diff --git a/odb-examples/qt/README b/odb-examples/qt/README new file mode 100644 index 0000000..390bab5 --- /dev/null +++ b/odb-examples/qt/README @@ -0,0 +1,81 @@ +This example shows how to persist objects that use Qt smart pointers, +containers, and value types with the help of the Qt profile library +(libodb-qt). + +The example consists of the following files: + +employee.hxx + Header file defining the 'employee' and 'employer' persistent classes. + We use QSharedPointer/QWeakPointer smart pointers provided by Qt (as + well as their lazy versions provided by the Qt profile library) to + establish a bidirectional employee-employer relationship. The QList + type is used to store the collection of employees employed by the + employer. We also use the QDateTime type to store the employee's date + of birth and QString to store the employee's first and last name. While + the employee's object id is QUuid and a QSet instance keeps track of + the employee's email addresses. Finally we use QByteArray to store the + employee's public key. + +employee-odb.hxx +employee-odb.ixx +employee-odb.cxx +employee.sql + The first three files contain the database support code and the last file + contains the database schema for the employee.hxx header. + + These files are generated by the ODB compiler from employee.hxx using the + following command line: + + odb --std c++11 -d <database> --profile qt --generate-schema \ + --generate-query --generate-session employee.hxx + + Where <database> stands for the database system we are using, for example, + 'pgsql'. Note that if using Qt 6, then need to specify --std c++17 option + instead of c++11. + + The --profile option is used to instruct the ODB compiler to load the Qt + profile. The --generate-session option is used to enable session support + for all the persistent classes in employee.hxx. + +database.hxx + Contains the createDatabase() function which instantiates the concrete + database class corresponding to the database system we are using. + +driver.cxx + Driver for the example. It includes the employee.hxx and employee-odb.hxx + headers to gain access to the persistent classes and their database support + code. It also includes database.hxx for the createDatabase() function + declaration. + + In main() the driver first calls createDatabase() to obtain the database + instance. It then creates a number of 'Employee' and 'Employer' objects and + persists them in the database. The next transaction loads all the employees + of a particular employer using the employee-employer relationship. Finally, + the driver performs a database query which uses data member of the Qt + QString and QDate types in its criterion. + +To compile and link the example manually from the command line we can use the +following commands (using PostgreSQL as an example; replace 'c++' with your +C++ compiler name): + +c++ -c employee-odb.cxx +c++ -DDATABASE_PGSQL -c driver.cxx +c++ -o driver driver.o employee-odb.o -lodb-qt -lodb-pgsql -lodb -lQtCore + +Note that libodb-qt doesn't link the QtCore library and it is the user's +responsibility to link it if necessary. + +To run the example we may first need to create the database schema (for some +database systems, such as SQLite, the schema is embedded into the generated +code which makes this step unnecessary). Using PostgreSQL as an example, this +can be achieved with the following command: + +psql --username=odb_test --dbname=odb_test -f employee.sql + +Here we use 'odb_test' as the database login and also 'odb_test' as the +database name. + +Once the database schema is ready, we can run the example (using PostgreSQL as +the database): + +./driver --user odb_test --database odb_test diff --git a/odb-examples/qt/buildfile b/odb-examples/qt/buildfile new file mode 100644 index 0000000..49085ec --- /dev/null +++ b/odb-examples/qt/buildfile @@ -0,0 +1,55 @@ +# file : qt/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') +{ + assert ($qt) \ + "Qt version should be configured for this example via config.odb_examples.qt variable" +} + +import meta_libs = libodb%lib{odb} +import meta_libs += libodb-qt%lib{odb-qt} +import meta_libs += "libQt$(qt_ver)Core"%lib{"Qt$(qt_ver)Core"} + +import libs = libodb-$database%lib{odb-$database} + +exe{driver}: {hxx cxx}{* -employee-odb} {hxx ixx cxx}{employee-odb} testscript + +# The metadata library target which we use to extract the poptions variable +# value for specifying the preprocessor options (-I, etc) on the ODB compiler +# command line. +# +libue{employee-meta}: $meta_libs + +exe{driver}: libue{employee-meta} $libs + +<{hxx ixx cxx}{employee-odb}>: hxx{employee} libue{employee-meta} $odb +{{ + pops = $cxx.lib_poptions($<[1]) + depdb hash $pops + + depdb dyndep --dyn-target --target-what 'generated schema' --format lines \ + -- echo ($sqlite ? '' : "$out_base/employee.sql") + + $odb --std ($qt_ver == 5 ? c++11 : c++17) \ + --database $database \ + --profile qt \ + --generate-schema \ + --generate-query \ + --generate-session \ + --output-dir $out_base \ + --table-prefix qt_ \ + "-I$src_base" $pops \ + $path($<[0]) +}} + +cxx.poptions =+ "-I$out_base" "-I$src_base" -DDATABASE_$ucase($database) + +# Testscript's run-time prerequisites. +# +# @@ BUILD2: Eventually we should be able to mark it as test.input once +# this is supported for testscript tests. +# +exe{driver}: ../alias{database-client}: include = adhoc + +testscript@./: schema = employee diff --git a/odb-examples/qt/database.hxx b/odb-examples/qt/database.hxx new file mode 100644 index 0000000..712cc4f --- /dev/null +++ b/odb-examples/qt/database.hxx @@ -0,0 +1,95 @@ +// file : qt/database.hxx +// copyright : not copyrighted - public domain + +// +// Create concrete database instance based on the DATABASE_* macros. +// + +#ifndef DATABASE_HXX +#define DATABASE_HXX + +#include <string> +#include <memory> // std::unique_ptr +#include <cstdlib> // std::exit +#include <iostream> + +#include <odb/database.hxx> + +#if defined(DATABASE_MYSQL) +# include <odb/mysql/database.hxx> +#elif defined(DATABASE_SQLITE) +# include <odb/connection.hxx> +# include <odb/transaction.hxx> +# include <odb/schema-catalog.hxx> +# include <odb/sqlite/database.hxx> +#elif defined(DATABASE_PGSQL) +# include <odb/pgsql/database.hxx> +#elif defined(DATABASE_ORACLE) +# include <odb/oracle/database.hxx> +#elif defined(DATABASE_MSSQL) +# include <odb/mssql/database.hxx> +#else +# error unknown database; did you forget to define the DATABASE_* macros? +#endif + +inline std::unique_ptr<odb::database> +createDatabase (int& argc, char* argv[]) +{ + using namespace std; + using namespace odb::core; + + if (argc > 1 && argv[1] == string ("--help")) + { + cout << "Usage: " << argv[0] << " [options]" << endl + << "Options:" << endl; + +#if defined(DATABASE_MYSQL) + odb::mysql::database::print_usage (cout); +#elif defined(DATABASE_SQLITE) + odb::sqlite::database::print_usage (cout); +#elif defined(DATABASE_PGSQL) + odb::pgsql::database::print_usage (cout); +#elif defined(DATABASE_ORACLE) + odb::oracle::database::print_usage (cout); +#elif defined(DATABASE_MSSQL) + odb::mssql::database::print_usage (cout); +#endif + + exit (0); + } + +#if defined(DATABASE_MYSQL) + unique_ptr<database> db (new odb::mysql::database (argc, argv)); +#elif defined(DATABASE_SQLITE) + unique_ptr<database> db ( + new odb::sqlite::database ( + argc, argv, false, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)); + + // Create the database schema. Due to bugs in SQLite foreign key + // support for DDL statements, we need to temporarily disable + // foreign keys. + // + { + connection_ptr c (db->connection ()); + + c->execute ("PRAGMA foreign_keys=OFF"); + + transaction t (c->begin ()); + schema_catalog::create_schema (*db); + t.commit (); + + c->execute ("PRAGMA foreign_keys=ON"); + } +#elif defined(DATABASE_PGSQL) + unique_ptr<database> db (new odb::pgsql::database (argc, argv)); +#elif defined(DATABASE_ORACLE) + unique_ptr<database> db (new odb::oracle::database (argc, argv)); +#elif defined(DATABASE_MSSQL) + unique_ptr<database> db ( + new odb::mssql::database (argc, argv, false, "TrustServerCertificate=yes")); +#endif + + return db; +} + +#endif // DATABASE_HXX diff --git a/odb-examples/qt/driver.cxx b/odb-examples/qt/driver.cxx new file mode 100644 index 0000000..97f7fe4 --- /dev/null +++ b/odb-examples/qt/driver.cxx @@ -0,0 +1,180 @@ +// file : qt/driver.cxx +// copyright : not copyrighted - public domain + +#include <iostream> + +#include <QtCore/QCoreApplication> + +#include <odb/database.hxx> +#include <odb/session.hxx> +#include <odb/transaction.hxx> + +#include "database.hxx" // createDatabase + +#include "employee.hxx" +#include "employee-odb.hxx" + +using namespace std; +using namespace odb::core; + +ostream& +operator<< (ostream& os, const QString& s) +{ + return os << s.toStdString (); +} + +int +main (int argc, char* argv[]) +{ + QCoreApplication app (argc, argv); + + try + { + unique_ptr<database> db (createDatabase (argc, argv)); + + // Create a few persistent objects. + // + { + // Simple Tech Ltd. + // + { + QSharedPointer<Employer> er (new Employer ("Simple Tech Ltd")); + + QSharedPointer<Employee> john ( + new Employee ("John", + "Doe", + QDate (1975, 1, 1), + QByteArray ("\0xF1\0x00\0x34\0x45\0x00\0xDE", 6), + er)); + + QSharedPointer<Employee> jane ( + new Employee ("Jane", + "Doe", + QDate (1976, 2, 2), + QByteArray ("\0xD7\0x00\0x14", 3), + er)); + + john->emails ().insert ("john_d@example.com"); + john->emails ().insert ("john.doe@simple.com"); + jane->emails ().insert ("jane_d@example.com"); + jane->emails ().insert ("jane.doe@simple.com"); + + // Set the inverse side of the employee-employer relationship. + // + er->employees ().push_back (john); + er->employees ().push_back (jane); + + transaction t (db->begin ()); + + db->persist (er); + + db->persist (john); + db->persist (jane); + + t.commit (); + } + + // Complex Systems Inc. + // + { + QSharedPointer<Employer> er (new Employer ("Complex Systems Inc")); + + QSharedPointer<Employee> john ( + new Employee ("John", + "Smith", + QDate (1977, 3, 3), + QByteArray ("\0x23\0xFD\0x8F\0x00", 4), + er)); + + QSharedPointer<Employee> jane ( + new Employee ("Jane", + "Smith", + QDate (1978, 4, 4), + QByteArray ("0x00\0x32\0x00\0x01\0x00", 5), + er)); + + john->emails ().insert ("john_s@example.com"); + john->emails ().insert ("john.smith@complex.com"); + jane->emails ().insert ("jane_s@example.com"); + jane->emails ().insert ("jane.smith@complex.com"); + + // Set the inverse side of the employee-employer relationship. + // + er->employees ().push_back (john); + er->employees ().push_back (jane); + + transaction t (db->begin ()); + + db->persist (er); + + db->persist (john); + db->persist (jane); + + t.commit (); + } + } + + // Load Simple Tech Ltd and print its employees. + // + { + session s; + transaction t (db->begin ()); + + QSharedPointer<Employer> stl (db->load<Employer> ("Simple Tech Ltd")); + + Employees& es (stl->employees ()); + + for (Employees::iterator i (es.begin ()); i != es.end (); ++i) + { + QLazyWeakPointer<Employee>& lwp (*i); + + // Load and lock the employee and his employer. + // + QSharedPointer<Employee> p (lwp.load ()); + QSharedPointer<Employer> pe (p->employer ().load ()); + + cout << p->first () << " " << p->last () << endl + << " born: " << p->born ().toString () << endl; + + for (Emails::const_iterator j (p->emails ().begin ()), + e (p->emails ().end ()); j != e; ++j) + { + cout << " email: " << *j << endl; + } + + cout << " public key length: " << p->publicKey ().size () << endl + << " employer: " << pe->name () << endl + << " id: " << p->id ().toString () << endl + << endl; + } + + t.commit (); + } + + // Search for Complex Systems Inc employees that were born before + // April 1978. + // + { + typedef odb::query<Employee> query; + typedef odb::result<Employee> result; + + session s; + transaction t (db->begin ()); + + result r (db->query<Employee> ( + query::employer->name == "Complex Systems Inc" && + query::born < QDate (1978, 4, 1))); + + for (result::iterator i (r.begin ()); i != r.end (); ++i) + cout << i->first () << " " << i->last () + << " " << i->born ().toString () << endl; + + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-examples/qt/employee.hxx b/odb-examples/qt/employee.hxx new file mode 100644 index 0000000..82bbcfb --- /dev/null +++ b/odb-examples/qt/employee.hxx @@ -0,0 +1,167 @@ +// file : qt/employee.hxx +// copyright : not copyrighted - public domain + +#ifndef EMPLOYEE_HXX +#define EMPLOYEE_HXX + +#include <QtCore/QString> +#include <QtCore/QByteArray> +#include <QtCore/QUuid> +#include <QtCore/QDateTime> +#include <QtCore/QSharedPointer> +#include <QtCore/QList> +#include <QtCore/QSet> + +#include <odb/core.hxx> + +#include <odb/qt/lazy-ptr.hxx> + +// Forward declarations. +// +class Employee; + +typedef QSet<QString> Emails; +typedef QList<QLazyWeakPointer<Employee> > Employees; + +#pragma db object +class Employer +{ +public: + + Employer (const QString& name) + : name_ (name) + { + } + + const QString& + name () const + { + return name_; + } + + const Employees& + employees () const + { + return employees_; + } + + Employees& + employees () + { + return employees_; + } + +private: + friend class odb::access; + + Employer () {} + + #pragma db id + QString name_; + + #pragma db value_not_null inverse(employer_) + Employees employees_; +}; + +#pragma db object +class Employee +{ +public: + + Employee (const QString& first, + const QString& last, + const QDate& born, + const QByteArray& publicKey, + QSharedPointer<Employer> employer) + : id_ (QUuid::createUuid ()), + first_ (first), + last_ (last), + born_ (born), + publicKey_ (publicKey), + employer_ (employer) + { + } + + // Id. + // + const QUuid& + id () const + { + return id_; + } + + // Name. + // + const QString& + first () const + { + return first_; + } + + const QString& + last () const + { + return last_; + } + + // Date of birth. + // + const QDate& + born () const + { + return born_; + } + + const Emails& + emails () const + { + return emails_; + } + + Emails& + emails () + { + return emails_; + } + + // Public key. + // + const QByteArray& + publicKey () const + { + return publicKey_; + } + + // Employer. + // + QLazySharedPointer<Employer> + employer () const + { + return employer_; + } + + void + employer (QSharedPointer<Employer> employer) + { + employer_ = employer; + } + +private: + friend class odb::access; + + Employee () {} + + #pragma db id + QUuid id_; + + QString first_; + QString last_; + QDate born_; + Emails emails_; + QByteArray publicKey_; + + #pragma db not_null + QLazySharedPointer<Employer> employer_; +}; + +#endif // EMPLOYEE_HXX diff --git a/odb-examples/qt/testscript b/odb-examples/qt/testscript new file mode 100644 index 0000000..d2b08a1 --- /dev/null +++ b/odb-examples/qt/testscript @@ -0,0 +1,13 @@ +# file : qt/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../database-options.testscript +.include ../$(database).testscript + ++if! $sqlite + $create_schema +end + +: basics +: +$* >| |