diff --git a/ActiveRecord/doc/ActiveRecordUserGuide.page b/ActiveRecord/doc/ActiveRecordUserGuide.page index 2038151f2..85bdebdc3 100644 --- a/ActiveRecord/doc/ActiveRecordUserGuide.page +++ b/ActiveRecord/doc/ActiveRecordUserGuide.page @@ -1,5 +1,5 @@ -POCO C++ Libraries ActiveRecord Framework POCO ActiveRecord User Guide +POCO ActiveRecord Framework !!!Introduction @@ -9,9 +9,10 @@ to relieve developers from having to write lots of boilerplate database query code for common operations like finding an object by ID, updating an object, deleting an object or running paged queries. As its name implies, the framework follows the well-known [[https://en.wikipedia.org/wiki/Active_record_pattern Active Record]] -architectural pattern. It's based on a code generator (named *Active Record Compiler*, +architectural pattern. It's based on a code generator (named <*ActiveRecord Compiler*>, or <[arc]>) and uses a convention-over-configuration approach. + !!!Getting Started The starting point for using the ActiveRecord framework is an XML file. @@ -21,9 +22,9 @@ generates corresponding header and source files defining and implementing the respective C++ classes, as well as type handlers for the POCO Data library. -Following is a first example for such an XML file. The file defines two +Following is an example for such an XML file. The file defines two classes, an `Employee` class (mapped to a table named `employees`), and -a `Role` class (mapped to a table names `roles`). +a `Role` class (mapped to a table named `roles`). @@ -46,6 +47,11 @@ There is a n:1 relationship between `Employee` and `Role` (each employee has exactly one role). Furthermore, each employee can optionally have a manager, which is again an `Employee`. +Properties named `id` are considered to be primary keys, unless a different +property has been designated the primary key (with the `key` attribute in +the `class` element). It's also possible to have objects without a primary key +or ID column (keyless active records). + The generated C++ classes will be in the `Sample` namespace, as specified in the <[project]> element. @@ -53,7 +59,7 @@ The definitions in the XML file correspond to the database schema built by the following <[CREATE TABLE]> statements: CREATE TABLE employees ( - id CHAR(36), + id CHAR(36) PRIMARY KEY, name VARCHAR(64), ssn VARCHAR(32), role INTEGER, @@ -67,6 +73,9 @@ by the following <[CREATE TABLE]> statements: ); ---- +If the database engine supports it, the `id` column of the `employees` table can be +an UUID as well. + Running the ActiveRecord Compiler with the above XML file (sample.xml) with the following statement: @@ -85,8 +94,8 @@ will create the following files in the current working directory: ---- The generated classes are derived from the Poco::ActiveRecord::ActiveRecord class -template and have accessor methods for all properties, as well as methods -for creating, updating and deleting instances in the database. +template and have accessor methods for all properties defined in the XML file, +as well as methods for creating, updating and deleting instances in the database. ActiveRecord objects are reference counted, and every generated class contains a `Ptr` type alias for an appropriate Poco::AutoPtr<>. @@ -264,6 +273,10 @@ allows some additional flexibility. const auto result = query.execute(); ---- +The lambda expression is passed a const reference to an ActiveRecord object and +must return a `bool`. If `true` is returned, the object is included in the result, +otherwise not. + !Relations Relations (defined in the XML file as properties with a `references` attribute) @@ -284,7 +297,7 @@ In the following sample, the `role` property is set with the key value, whereas pEmployee->create(pContext); ---- -!Auto-increment Keys and Auto-generated UUIDs on Insert +!Auto-Increment Keys and Auto-Generated UUIDs on Insert ActiveRecord supports auto-incrementing keys when inserting an ActiveRecord. T o enable this feature, the `autoIncrementID` attribute in the `class` element needs @@ -297,7 +310,16 @@ mechanisms. When inserting an ActiveRecord with an all-null UUID, a random UUID will be generated before executing the `INSERT` statement. -!!!XML Reference +!Keyless Active Records + +It is possible to define classes without an ID or primary key property. For these objects, +no `find()` method will be generated, and updating these objects is also not possible +(`update()` will throw a Poco::NotImplementedException). + +Keyless ActiveRecord objects can be retrieved by executing a Poco::ActiveRecord::Query. + + +!!!Compiler XML Reference !!Supported Data Types diff --git a/ActiveRecord/include/Poco/ActiveRecord/Query.h b/ActiveRecord/include/Poco/ActiveRecord/Query.h index 78c50011e..486387c29 100644 --- a/ActiveRecord/include/Poco/ActiveRecord/Query.h +++ b/ActiveRecord/include/Poco/ActiveRecord/Query.h @@ -28,6 +28,15 @@ namespace ActiveRecord { template class Query + /// A Query is used to retrieve ActiveRecord objects from a table. + /// + /// As the name implies, Query supports selection of database rows + /// based on a WHERE clause (see where()). Furthermore, results can + /// be sorted (see orderBy()) and filtered based on a lambda expression + /// (see filter()). + /// + /// Optional result paging is controlled by offest() and limit(). + /// The total number of results is available via totalResults(). { public: explicit Query(Context::Ptr pContext): @@ -43,12 +52,20 @@ public: Query& operator = (const Query&) = delete; Query& where(const std::string& clause) + /// Specify a WHERE clause (without the WHERE keyword) + /// to select only rows matching the clause. + /// + /// Placeholders (?) can be used in the clause. For each + /// placeholder, an actual value must be bound before + /// the query is executed (see bind()). { _select << " WHERE " << fixPlaceholders(clause); return *this; } Query& orderBy(const std::string& order) + /// Specify a column name and optional direction (ASC, DESC) + /// to order the result by. { _select << " ORDER BY " << order; return *this; @@ -56,24 +73,34 @@ public: template Query& bind(const T& value) + /// Bind a value to a placeholder in the where clause. { _select, Poco::Data::Keywords::bind(value); return *this; } Query& offset(std::size_t offset) + /// Specify the index or offset of the first row + /// to return for paging. { _offset = offset; return *this; } Query& limit(std::size_t limit) + /// specify the maximum number of rows to return for paging. { _limit = limit; return *this; } Query& filter(const std::function& fn) + /// Specify a lambda expression for filtering results. + /// + /// The lamda takes a const reference to the ActiveRecord + /// (template argument) as parameter and must return a + /// bool. If the lambda returns true, the respective ActiveRecord + /// is included in the query result. { _filter = fn; return *this; @@ -86,6 +113,8 @@ public: } std::vector execute() + /// Execute the query and return a vector with the + /// results. { std::vector result; @@ -115,11 +144,15 @@ public: } std::size_t totalResults() const + /// In case of a paged query, returns the total number of results + /// that would be returned without paging. { return _totalResults; } void reset() + /// Resets the query so that it can be executed again, with + /// potentially different parameters. { _offset = 0; _limit = 0; diff --git a/release/spec/all.release b/release/spec/all.release index c896832aa..55acdf124 100644 --- a/release/spec/all.release +++ b/release/spec/all.release @@ -11,3 +11,5 @@ PageCompiler PageCompiler/File2Page MongoDB Redis +ActiveRecord +ActiveRecord/Compiler