mirror of
https://github.com/silverqx/TinyORM.git
synced 2026-01-06 10:59:31 -06:00
769 lines
24 KiB
Plaintext
769 lines
24 KiB
Plaintext
Todos to check in TinyOrm:
|
|
--------------------------
|
|
|
|
- QueryBuilder::insertGetId() allows insert with empty attributes, also Model::performInsert()
|
|
when incrementing == true, but all other insert methods don't, it's big inconsistency, unify it
|
|
|
|
|
|
Documentation TinyOrm Todos:
|
|
----------------------------
|
|
|
|
- how to refer NULL in docs, for now I leave it NULL
|
|
|
|
|
|
TODOs which look bad in code:
|
|
-----------------------------
|
|
|
|
- future add onDelete (and similar) callback feature
|
|
|
|
/*! Delete records from the database. */
|
|
void deleteModel()
|
|
{
|
|
// TODO future add onDelete (and similar) callback feature silverqx
|
|
// if (isset($this->onDelete)) {
|
|
// return call_user_func($this->onDelete, $this);
|
|
// }
|
|
|
|
return toBase().deleteRow();
|
|
}
|
|
|
|
- add c++20 compiler check, something like:
|
|
#ifdef __cplusplus
|
|
# if __cplusplus < 201103L && !defined(Q_CC_MSVC)
|
|
# error Qt requires a C++11 compiler and yours does not seem to be that.
|
|
# endif
|
|
#endif
|
|
|
|
- check this in cmake build:
|
|
#include(GenerateExportHeader)
|
|
#_test_compiler_hidden_visibility()
|
|
|
|
|
|
Todo categories:
|
|
----------------
|
|
|
|
Common:
|
|
|
|
- api different : different api than Laravel's Eloquent
|
|
- check : something to find out 🤔
|
|
- concept : add concept or constraint
|
|
- docs : document code or update markdown documentation
|
|
- desirable : feature which is extremely wanted
|
|
- dilemma : some sort of a fuckup
|
|
- duplicate : duplicate code
|
|
- feature : some feature to implement, perpend before feature described below
|
|
- future : task which has lower priority, because still much to do
|
|
- mistake : bad decision during prototyping 😭
|
|
- move : c++ move semantics
|
|
- mystery : don't know why that stuff is happening, find out what's up
|
|
- now : do it before commit
|
|
- next : next thing in the row to do after commit
|
|
- overflow : add check code, eg when size_t to int conversion
|
|
- perf : performance
|
|
- production : check before deploy to production
|
|
- reliability : make things more robust and reliable
|
|
- repeat : tasks that should I make from time to time
|
|
- security : self explaining
|
|
- study : don't know how something works, need to check up
|
|
- sync : synchronization in multi thread environment silverqx
|
|
- test : tasks in auto tests
|
|
- types : juggling with c++ types
|
|
|
|
Features related/to implement:
|
|
|
|
- aggregates : aggregate values like count, max, min, avg and sum
|
|
- castable : attributes casting
|
|
- default attributes : Default Attribute Values
|
|
- dilemma primarykey : different types for primary keys
|
|
- expression : DB::raw() support in the query builder
|
|
- events : event system
|
|
- ga : github actions
|
|
- guarded : related to the mass assignable feature
|
|
- json columns : JSON support
|
|
- logging : logging related
|
|
- multidriver : task related to adding support for another drivers PostgreSQL, SQLite and SQL Server
|
|
- pivot : pivot table in the many-to-many relationship
|
|
- postgres : specific to PostgreSQL server
|
|
- qt6 : related to Qt6 upgrade or compatibility
|
|
- read/write connection : read/write connection
|
|
- relations : relations related 🤓
|
|
- savepoints : database savepoints
|
|
- scopes : query scopes
|
|
- table prefix : table prefix in the query grammar
|
|
|
|
|
|
Versions info:
|
|
--------------
|
|
|
|
This is laravel/framework version, not laravel/laravel version:
|
|
|
|
- I have cloned repository at - E:\htdocs\laravel-src-master
|
|
- based on Laravel v8.26.1
|
|
- upgrade to Laravel v8.41.0 ( 15.5.2021, but I didn't merged/reflected new changes to TinyORM )
|
|
- upgrade to Laravel v8.80.0 ( 19.1.2021, upgrade from v8.41.0 )
|
|
- compare URLs (remove after merge):
|
|
- https://github.com/laravel/framework/compare/v8.26.1...v8.41.0
|
|
- https://github.com/laravel/framework/compare/v8.41.0...v8.80.0
|
|
|
|
|
|
Maintenance:
|
|
------------
|
|
|
|
- from time to time try:
|
|
- compile without PCH
|
|
- compile with Qt6, I have still problem with clazy
|
|
|
|
|
|
RegExs:
|
|
-------
|
|
|
|
- const data members:
|
|
(?<![\(\)])(const) +.* +\bm_.*\b( +.*)?;$
|
|
(?<![\(\)])(const) +.* +\bm_.*\b +=
|
|
(?<![\(\)])(const) +.* +\bm_.*\b +{
|
|
- const data member references:
|
|
(?<![\(\)])(const) +.* +&\bm_.*\b( +.*)?;$
|
|
- all exceptions:
|
|
throw (.*::)?\w+(E|_error)
|
|
|
|
|
|
Powershell commands:
|
|
--------------------
|
|
|
|
- export todos to csv:
|
|
Get-ChildItem -Path *.cpp,*.hpp -Recurse | sls -Pattern ' (TODO|NOTE|FIXME|BUG|WARNING|CUR|FEATURE|TEST|FUTURE) ' -CaseSensitive | % { $_.Line = $_.Line.Trim().TrimStart('// '); return $_; } | select Line,LineNumber,Path | Export-Csv todos.csv -Delimiter ';' -NoTypeInformation
|
|
|
|
- search in todos:
|
|
Get-ChildItem -Path *.cpp,*.hpp -Recurse | sls -Pattern ' (TODO|NOTE|FIXME|BUG|WARNING|CUR|FEATURE|TEST|FUTURE) ' -CaseSensitive | % { $_.Line = $_.Line.Trim().TrimStart('// '); return $_; } | where Line -Match 'pch' | select Line,LineNumber,Path | ft -AutoSize
|
|
|
|
- filter out executed queries:
|
|
Get-Content .\tmp.sql | sls -Pattern '^(Executed prepared query)' | Set-Content executed_queries.sql
|
|
|
|
- TinyOrmPlayground - run InvokeXTimes.ps1 on Linux:
|
|
stp && sq5
|
|
export LD_LIBRARY_PATH=../../../TinyOrm/TinyOrm-builds-qmake/build-TinyOrm-Desktop_Qt_5_15_2_GCC_64bit_ccache-Debug/src
|
|
pwsh -NoLogo -NoProfile -File InvokeXTimes.ps1 2 ../../../TinyOrmPlayground/TinyOrmPlayground-builds-qmake/build-TinyOrmPlayground-Desktop_Qt_5_15_2_GCC_64bit_ccache-Debug/TinyOrmPlayground
|
|
|
|
- TinyORM - run InvokeXTimes.ps1 on Linux:
|
|
stp && sq5 && cdtq && cd build-TinyOrm-Desktop_Qt_5_15_2_GCC_64bit_ccache-Debug
|
|
export LD_LIBRARY_PATH=./src:./tests/TinyUtils${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH
|
|
export PATH=$HOME/Code/c/TinyORM/tools:$PATH
|
|
InvokeXTimes.ps1
|
|
pwsh -NoLogo -NoProfile -File InvokeXTimes.ps1 100
|
|
|
|
|
|
Powershell Clang analyzers:
|
|
---------------------------
|
|
|
|
qa-lint-tinyorm-qt5.ps1 is tailor-made for TinyORM project.
|
|
|
|
qa-clang-tidy.ps1, qa-clazy-standalone.ps1, qa-clazy-standalone-st.ps1 are more general, they can be used with any project, "-st" script calls raw clazy-standalone.exe.
|
|
|
|
run-clang-tidy.ps1, run-clazy-standalone.ps1 are raw Powershell wrappers around python run-clang-tidy/run-clazy-standalone.py python scripts.
|
|
|
|
|
|
cmake build commands:
|
|
---------------------
|
|
|
|
vcvars64.ps1
|
|
cd E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-cmake\
|
|
cmake.exe -S E:/c/qMedia/TinyOrm/TinyOrm -B E:/c/qMedia/TinyOrm/TinyOrm-builds-cmake/build-cmake -GNinja `
|
|
-DCMAKE_BUILD_TYPE:STRING=Debug `
|
|
-DCMAKE_TOOLCHAIN_FILE:PATH=E:/c/qMedia/vcpkg/scripts/buildsystems/vcpkg.cmake
|
|
|
|
cmake --build . --target all
|
|
|
|
- generate Graphviz dependency image:
|
|
|
|
cmake.exe -S E:/c/qMedia/TinyOrm/TinyOrm -B E:/c/qMedia/TinyOrm/TinyOrm-builds-cmake/build-cmake -GNinja `
|
|
-DCMAKE_BUILD_TYPE:STRING=Debug `
|
|
-DCMAKE_TOOLCHAIN_FILE:PATH=E:/c/qMedia/vcpkg/scripts/buildsystems/vcpkg.cmake `
|
|
--graphviz=E:/c/qMedia/TinyOrm/TinyOrm-builds-cmake/build-cmake/graph/graph.dot; `
|
|
`
|
|
dot -Tpng -o .\graph\graph.png .\graph\graph.dot; `
|
|
.\graph\graph.png
|
|
|
|
- running ctest
|
|
|
|
E:\c\qMedia\TinyOrm\TinyOrm\tests\auto\utils\testdata\dotenv.ps1
|
|
$env:Path = "E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-TinyOrm-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug-cmake;E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-TinyOrm-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug-cmake\tests\auto\utils;" + $env:Path
|
|
ctest
|
|
ctest --progress
|
|
|
|
- some debug output:
|
|
|
|
cmake --trace-expand --trace-source=tests/auto/unit/orm/query/mysql_querybuilder/CMakeLists.txt -LA ..
|
|
cmake -LA .
|
|
|
|
- full build command, not needed, I leave it here as a shortcut:
|
|
|
|
cmake.exe -S E:/c/qMedia/TinyOrm/TinyOrm -B E:/c/qMedia/TinyOrm/TinyOrm-builds-cmake/build-cmake -GNinja `
|
|
"-DCMAKE_BUILD_TYPE:STRING=Debug" `
|
|
"-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=E:/Qt/Tools/QtCreator/share/qtcreator/package-manager/auto-setup.cmake" `
|
|
"-DQT_QMAKE_EXECUTABLE:STRING=E:/Qt/5.15.2/msvc2019_64/bin/qmake.exe" `
|
|
"-DCMAKE_PREFIX_PATH:STRING=E:/Qt/5.15.2/msvc2019_64" `
|
|
"-DCMAKE_C_COMPILER:STRING=C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30037/bin/HostX64/x64/cl.exe" `
|
|
"-DCMAKE_CXX_COMPILER:STRING=C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30037/bin/HostX64/x64/cl.exe" ` "-DCMAKE_TOOLCHAIN_FILE:PATH=E:/c/qMedia/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
|
|
|
- put TinyOrm and TinyUtils libraries on the system path:
|
|
|
|
$env:Path = "E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-cmake;E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-cmake\tests\auto\utils;" + $env:Path
|
|
|
|
|
|
TinyORM docs github pages:
|
|
--------------------------
|
|
|
|
- npm run clear
|
|
|
|
Clear a Docusaurus site's generated assets, caches, build artifacts.
|
|
We recommend running this command before reporting bugs, after upgrading versions, or anytime you have issues with your Docusaurus site.
|
|
|
|
- deploy:
|
|
|
|
.\dotenv.ps1
|
|
npm run deploy; echo "Algolia Rebuild"; sleep 30; .\algolia_rebuild.ps1
|
|
|
|
- local development:
|
|
|
|
npm start
|
|
npm start -- --no-open
|
|
|
|
- update Algolia index by DocSearch:
|
|
|
|
.\dotenv.ps1
|
|
.\algolia_rebuild.ps1
|
|
|
|
|
|
CMake Guidelines:
|
|
-----------------
|
|
|
|
All are snake-case unless otherwise specified.
|
|
|
|
- variable names:
|
|
- global variables: tiny_
|
|
- local variables: preferred camelCase or snake-case
|
|
- function parameters: lowercase and optional tiny_ prefix
|
|
- cmake_parse_arguments: TINY_
|
|
- option variables: upper case without prefix
|
|
- cached variables: TINY_
|
|
- function names has the tiny_ prefix
|
|
- compile definitions prefix by project eg. TINYORM_, TINYUTILS_
|
|
|
|
|
|
UBSan:
|
|
------
|
|
|
|
QMAKE_CXXFLAGS += -O1 -fsanitize=undefined
|
|
QMAKE_LFLAGS += -fsanitize=undefined
|
|
|
|
QMAKE_CXXFLAGS += -O1 -fsanitize=nullability -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds
|
|
QMAKE_LFLAGS += -fsanitize=nullability -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds
|
|
|
|
|
|
constructor copy/move snippet:
|
|
------------------------------
|
|
|
|
Add code below to class you want to optimize and set breakpoints inside and you will see what cause what 😎:
|
|
|
|
Torrent(const Torrent &torrent)
|
|
: Model(torrent)
|
|
{
|
|
qDebug() << "Torrent copy ctor";
|
|
}
|
|
Torrent(Torrent &&torrent)
|
|
: Model(std::move(torrent))
|
|
{
|
|
qDebug() << "Torrent move ctor";
|
|
}
|
|
Torrent &operator=(const Torrent &torrent)
|
|
{
|
|
Model::operator=(torrent);
|
|
qDebug() << "Torrent copy assign";
|
|
return *this;
|
|
}
|
|
Torrent &operator=(Torrent &&torrent)
|
|
{
|
|
Model::operator=(std::move(torrent));
|
|
qDebug() << "Torrent move assign";
|
|
return *this;
|
|
}
|
|
|
|
|
|
conversions:
|
|
------------
|
|
|
|
Makes possible to assign QVector<AttributeItem> to the Model,
|
|
or implicitly converts a QVector<AttributeItem> to Model:
|
|
|
|
Model(const QVector<AttributeItem> &attributes);
|
|
Model(QVector<AttributeItem> &&attributes);
|
|
|
|
--
|
|
Allows initialize the Model with QVector<AttributeItem>:
|
|
|
|
Model(std::initializer_list<AttributeItem> attributes)
|
|
: Model(QVector<AttributeItem> {attributes.begin(), attributes.end()})
|
|
{}
|
|
|
|
--
|
|
Makes possible to assign the Model to the QVector<AttributeItem>,
|
|
or converts the Model to the QVector<AttributeItem>:
|
|
|
|
operator QVector<AttributeItem>() const;
|
|
|
|
|
|
Ranges transform:
|
|
-----------------
|
|
|
|
const auto relationToWithItem = [](const auto &relation) -> WithItem
|
|
{
|
|
return WithItem {relation};
|
|
};
|
|
|
|
builder->with(relations | ranges::views::transform(relationToWithItem)
|
|
| ranges::to<QVector<WithItem>>());
|
|
|
|
|
|
DatabaseConnection config:
|
|
--------------------------
|
|
|
|
QHash<QString, QVariant> config {
|
|
// {"driver", "mysql"},
|
|
// {"url", qEnvironmentVariable("DATABASE_URL")},
|
|
// {"url", qEnvironmentVariable("MYSQL_DATABASE_URL")},
|
|
{"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},
|
|
{"port", qEnvironmentVariable("DB_MYSQL_PORT", "3306")},
|
|
{"database", qEnvironmentVariable("DB_MYSQL_DATABASE", "")},
|
|
{"username", qEnvironmentVariable("DB_MYSQL_USERNAME", "root")},
|
|
{"password", qEnvironmentVariable("DB_MYSQL_PASSWORD", "")},
|
|
// {"unix_socket", qEnvironmentVariable("DB_MYSQL_SOCKET", "")},
|
|
{"charset", qEnvironmentVariable("DB_MYSQL_CHARSET", "utf8mb4")},
|
|
{"collation", qEnvironmentVariable("DB_MYSQL_COLLATION", "utf8mb4_unicode_ci")},
|
|
// {"collation", qEnvironmentVariable("DB_MYSQL_COLLATION", "utf8mb4_0900_ai_ci")},
|
|
// {"timezone", "+00:00"},
|
|
// {"prefix", ""},
|
|
// {"prefix_indexes", true},
|
|
{"strict", true},
|
|
// {"engine", {}},
|
|
{"options", ""},
|
|
};
|
|
|
|
QHash<QString, QVariant> config {
|
|
{"driver", "QSQLITE"},
|
|
{"database", qEnvironmentVariable("DB_SQLITE_DATABASE", "")},
|
|
{"prefix", ""},
|
|
{"options", QVariantHash()},
|
|
{"foreign_key_constraints", qEnvironmentVariable("DB_SQLITE_FOREIGN_KEYS",
|
|
"true")},
|
|
{"check_database_exists", true},
|
|
};
|
|
|
|
|
|
DatabaseConnection debug code:
|
|
------------------------------
|
|
|
|
{
|
|
auto [ok, query] = select("select @@session.time_zone, @@global.time_zone");
|
|
while(query.next()) {
|
|
qDebug().nospace() << query.value(0).toString() << "\n"
|
|
<< query.value(1).toString();
|
|
}
|
|
}
|
|
|
|
{
|
|
auto [ok, query] = select("select @@session.character_set_client, @@session.character_set_connection, "
|
|
"@@session.character_set_results, @@session.collation_connection");
|
|
while(query.next()) {
|
|
qDebug().nospace() << query.value(0).toString() << "\n"
|
|
<< query.value(1).toString() << "\n"
|
|
<< query.value(2).toString() << "\n"
|
|
<< query.value(3).toString();
|
|
}
|
|
}
|
|
|
|
{
|
|
auto [ok, query] = select("select @@global.character_set_client, @@global.character_set_connection, "
|
|
"@@global.character_set_results, @@global.collation_connection");
|
|
while(query.next()) {
|
|
qDebug().nospace() << query.value(0).toString() << "\n"
|
|
<< query.value(1).toString() << "\n"
|
|
<< query.value(2).toString() << "\n"
|
|
<< query.value(3).toString();
|
|
}
|
|
}
|
|
|
|
{
|
|
auto [ok, query] = select("select @@global.sql_mode, @@session.sql_mode");
|
|
while(query.next()) {
|
|
qDebug().nospace() << query.value(0).toString() << "\n"
|
|
<< query.value(1).toString();
|
|
}
|
|
}
|
|
|
|
|
|
tmp notes:
|
|
----------
|
|
|
|
message(-------)
|
|
message(XXX config.pri)
|
|
message(PWD: $$PWD)
|
|
message(OUT_PWD: $$OUT_PWD)
|
|
message(_PRO_FILE_PWD_: $$_PRO_FILE_PWD_)
|
|
message(INCLUDEPATH: $$INCLUDEPATH)
|
|
message(-------)
|
|
|
|
|
|
tmp notes - Queryable columns:
|
|
------------------------------
|
|
|
|
template<SubQuery T>
|
|
struct Queryable
|
|
{
|
|
QString as;
|
|
// std::variant<std::function<void(Orm::QueryBuilder &)>> queryable;
|
|
T queryable;
|
|
};
|
|
|
|
Queryable(Orm::QueryBuilder &) -> Queryable<Orm::QueryBuilder &>;
|
|
|
|
|
|
/*! Set the columns to be selected. */
|
|
template<SubQuery T>
|
|
Builder &select(const QVector<Queryable<T>> &columns)
|
|
// Builder &select(const QVector<Queryable> &columns)
|
|
{
|
|
clearColumns();
|
|
|
|
for (const auto &q : columns)
|
|
selectSub(q.queryable, q.as);
|
|
|
|
return *this;
|
|
}
|
|
/*! Set the column to be selected. */
|
|
// template<SubQuery T>
|
|
// Builder &select(const Column &column);
|
|
// /*! Add new select columns to the query. */
|
|
// template<SubQuery T>
|
|
// Builder &addSelect(const QVector<Column> &columns);
|
|
// /*! Add a new select column to the query. */
|
|
// template<SubQuery T>
|
|
// Builder &addSelect(const Column &column);
|
|
|
|
|
|
/*! Makes "from" fetch from a subquery. */
|
|
// template<SubQuery T>
|
|
// Builder &whereSub(T &&query, const QVariant &value)
|
|
// {
|
|
// /* If the column is a Closure instance and there is an operator value, we will
|
|
// assume the developer wants to run a subquery and then compare the result
|
|
// of that subquery with the given value that was provided to the method. */
|
|
// auto [queryString, bindings] = createSub(std::forward<T>(query));
|
|
|
|
// addBinding(bindings, BindingType::WHERE);
|
|
|
|
// return where(Expression(QStringLiteral("(%1)").arg(queryString)),
|
|
// QStringLiteral("="), value);
|
|
// }
|
|
|
|
|
|
Model copy ctor:
|
|
----------------
|
|
|
|
template<typename Derived, AllRelationsConcept ...AllRelations>
|
|
Model<Derived, AllRelations...>::Model(const Model &model)
|
|
: exists(model.exists)
|
|
, u_table(model.u_table)
|
|
, u_connection(model.u_connection)
|
|
, u_incrementing(model.u_incrementing)
|
|
, u_primaryKey(model.u_primaryKey)
|
|
, u_relations(model.u_relations)
|
|
, u_with(model.u_with)
|
|
, m_attributes(model.m_attributes)
|
|
, m_original(model.m_original)
|
|
, m_changes(model.m_changes)
|
|
, m_attributesHash(model.m_attributesHash)
|
|
, m_originalHash(model.m_originalHash)
|
|
, m_changesHash(model.m_changesHash)
|
|
, m_relations(model.m_relations)
|
|
, u_touches(model.u_touches)
|
|
, m_pivots(model.m_pivots)
|
|
, u_timestamps(model.u_timestamps)
|
|
{}
|
|
|
|
|
|
AssignmentList:
|
|
---------------
|
|
|
|
I want to save this pattern:
|
|
|
|
struct AssignmentListItem
|
|
{
|
|
QString column;
|
|
QVariant value;
|
|
};
|
|
|
|
class AssignmentList final : public QVector<AssignmentListItem>
|
|
{
|
|
// Inherit all the base class constructors, wow 😲✨
|
|
using QVector<AssignmentListItem>::QVector;
|
|
|
|
public:
|
|
AssignmentList(const QVariantHash &variantHash)
|
|
{
|
|
auto itHash = variantHash.constBegin();
|
|
while (itHash != variantHash.constEnd()) {
|
|
*this << AssignmentListItem({itHash.key(), itHash.value()});
|
|
++itHash;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
EntityManager.hpp:
|
|
------------------
|
|
|
|
#ifndef ENTITYMANAGER_H
|
|
#define ENTITYMANAGER_H
|
|
|
|
#include "orm/databaseconnection.hpp"
|
|
#include "orm/repositoryfactory.hpp"
|
|
|
|
#ifdef TINYORM_COMMON_NAMESPACE
|
|
namespace TINYORM_COMMON_NAMESPACE
|
|
{
|
|
#endif
|
|
namespace Orm
|
|
{
|
|
|
|
/*! The EntityManager is the central access point to ORM functionality. */
|
|
class SHAREDLIB_EXPORT EntityManager final
|
|
{
|
|
Q_DISABLE_COPY(EntityManager)
|
|
|
|
public:
|
|
EntityManager(const QVariantHash &config);
|
|
EntityManager(DatabaseConnection &connection);
|
|
~EntityManager();
|
|
|
|
/*! Factory method to create EntityManager instances. */
|
|
static EntityManager create(const QVariantHash &config);
|
|
|
|
/*! Gets the repository for an entity class. */
|
|
template<typename Repository>
|
|
QSharedPointer<Repository> getRepository() const;
|
|
|
|
/*! Create a new QSqlQuery. */
|
|
QSqlQuery query() const;
|
|
/*! Get a new query builder instance. */
|
|
QSharedPointer<QueryBuilder> queryBuilder() const;
|
|
/*! Check database connection and show warnings when the state changed. */
|
|
bool pingDatabase();
|
|
/*! Start a new database transaction. */
|
|
bool transaction();
|
|
/*! Commit the active database transaction. */
|
|
bool commit();
|
|
/*! Rollback the active database transaction. */
|
|
bool rollback();
|
|
/*! Start a new named transaction savepoint. */
|
|
bool savepoint(const QString &id);
|
|
/*! Rollback to a named transaction savepoint. */
|
|
bool rollbackToSavepoint(const QString &id);
|
|
|
|
/*! Get underlying database connection. */
|
|
inline DatabaseConnection &connection() const
|
|
{ return m_db; }
|
|
|
|
protected:
|
|
/*! Factory method to create DatabaseConnection instances. */
|
|
static DatabaseConnection &
|
|
createConnection(const QVariantHash &config);
|
|
|
|
private:
|
|
/*! The database connection used by the EntityManager. */
|
|
DatabaseConnection &m_db;
|
|
/*! The repository factory used to create dynamic repositories. */
|
|
RepositoryFactory m_repositoryFactory;
|
|
};
|
|
|
|
template<typename Repository>
|
|
QSharedPointer<Repository> EntityManager::getRepository() const
|
|
{
|
|
return m_repositoryFactory.getRepository<Repository>();
|
|
}
|
|
|
|
} // namespace Orm
|
|
#ifdef TINYORM_COMMON_NAMESPACE
|
|
} // namespace TINYORM_COMMON_NAMESPACE
|
|
#endif
|
|
|
|
#endif // ENTITYMANAGER_H
|
|
|
|
|
|
EntityManager.cpp:
|
|
------------------
|
|
|
|
#include "orm/entitymanager.hpp"
|
|
|
|
#include <QtSql/QSqlQuery>
|
|
|
|
#ifdef TINYORM_COMMON_NAMESPACE
|
|
namespace TINYORM_COMMON_NAMESPACE
|
|
{
|
|
#endif
|
|
namespace Orm
|
|
{
|
|
|
|
/*!
|
|
\class EntityManager
|
|
\brief The EntityManager class manages repositories and a connection
|
|
to the database.
|
|
|
|
\ingroup database
|
|
\inmodule Export
|
|
|
|
EntityManager is the base class to work with the database, it creates
|
|
and manages repository classes by helping with the RepositoryFactory
|
|
class.
|
|
Creates the database connection which is represented by
|
|
DatabaseConnection class.
|
|
EntityManager should be used in controllers ( currently TorrentExporter
|
|
is like a controller class ), services, and repository classes to access
|
|
the database. There is no need to use the QSqlDatabase or the
|
|
DatabaseConnection classes directly.
|
|
EntityManager is also injected into a repository and a service
|
|
classes constructors.
|
|
The circular dependency problem is solved by including entitymanager.hpp
|
|
in the baserepository.hpp file.
|
|
*/
|
|
|
|
EntityManager::EntityManager(const QVariantHash &config)
|
|
: m_db(createConnection(config))
|
|
, m_repositoryFactory(*this)
|
|
{}
|
|
|
|
EntityManager::EntityManager(DatabaseConnection &connection)
|
|
: m_db(connection)
|
|
, m_repositoryFactory(*this)
|
|
{}
|
|
|
|
EntityManager::~EntityManager()
|
|
{
|
|
DatabaseConnection::freeInstance();
|
|
}
|
|
|
|
EntityManager EntityManager::create(const QVariantHash &config)
|
|
{
|
|
return EntityManager(createConnection(config));
|
|
}
|
|
|
|
QSqlQuery EntityManager::query() const
|
|
{
|
|
return m_db.query();
|
|
}
|
|
|
|
QSharedPointer<QueryBuilder> EntityManager::queryBuilder() const
|
|
{
|
|
return m_db.query();
|
|
}
|
|
|
|
bool EntityManager::pingDatabase()
|
|
{
|
|
return m_db.pingDatabase();
|
|
}
|
|
|
|
bool EntityManager::transaction()
|
|
{
|
|
return m_db.transaction();
|
|
}
|
|
|
|
bool EntityManager::commit()
|
|
{
|
|
return m_db.commit();
|
|
}
|
|
|
|
bool EntityManager::rollback()
|
|
{
|
|
return m_db.rollback();
|
|
}
|
|
|
|
bool EntityManager::savepoint(const QString &id)
|
|
{
|
|
return m_db.savepoint(id);
|
|
}
|
|
|
|
bool EntityManager::rollbackToSavepoint(const QString &id)
|
|
{
|
|
return m_db.rollbackToSavepoint(id);
|
|
}
|
|
|
|
DatabaseConnection &
|
|
EntityManager::createConnection(const QVariantHash &config)
|
|
{
|
|
return DatabaseConnection::create(config.find("database").value().toString(),
|
|
config.find("prefix").value().toString(),
|
|
config);
|
|
}
|
|
|
|
} // namespace Orm
|
|
#ifdef TINYORM_COMMON_NAMESPACE
|
|
} // namespace TINYORM_COMMON_NAMESPACE
|
|
#endif
|
|
|
|
|
|
Test Column expressions code, just swap groupBy
|
|
-----
|
|
|
|
auto q1 = Torrent::find(1)->torrentFiles()->groupBy({"xyz", "abc"}).toSql();
|
|
qDebug() << q1;
|
|
auto q2 = Torrent::find(1)->torrentFiles()->groupBy("xyz").toSql();
|
|
qDebug() << q2;
|
|
auto q = Torrent::find(1)->torrentFiles()->groupBy("abc", "def").toSql();
|
|
qDebug() << q;
|
|
|
|
auto t1 = Torrent::find(1)->torrentFiles()->groupBy({DB::raw("xyz"), "abc"}).toSql();
|
|
qDebug() << t1;
|
|
auto t2 = Torrent::find(1)->torrentFiles()->groupBy(DB::raw("xyz")).toSql();
|
|
qDebug() << t2;
|
|
auto t = Torrent::find(1)->torrentFiles()->groupBy("abc", DB::raw("def")).toSql();
|
|
qDebug() << t;
|
|
|
|
QString s1("abc");
|
|
const QString s2("fgh");
|
|
auto q3 = Torrent::find(1)->torrentFiles()->groupBy(s1, s2).toSql();
|
|
qDebug() << q3;
|
|
auto q4 = Torrent::find(1)->torrentFiles()->groupBy(std::move(s1), s2).toSql();
|
|
qDebug() << q4;
|
|
|
|
const QString s3("jkl");
|
|
auto t3 = Torrent::find(1)->torrentFiles()->groupBy(s3, DB::raw(s2)).toSql();
|
|
qDebug() << t3;
|
|
auto t4 = Torrent::find(1)->torrentFiles()->groupBy(std::move(s3), DB::raw(s2)).toSql();
|
|
qDebug() << t4;
|
|
|
|
|
|
Invoke-Tests.ps1:
|
|
-----------------
|
|
|
|
- 100 times run
|
|
- 28. dec 2021
|
|
- Windows 10:
|
|
- Qt 5.15.2 ; msvc 16.11.8 x64
|
|
- debug build
|
|
All AutoTests Execution time : 792519ms
|
|
All AutoTests Average Execution time : 7925ms
|
|
|
|
- Qt 6.2.1 ; msvc 16.11.8 x64
|
|
- debug build
|
|
All AutoTests Execution time : 986531ms
|
|
All AutoTests Average Execution time : 9865ms
|
|
|
|
- Gentoo:
|
|
- Qt 5.15.2 ; GCC 11.2 x64 ccache
|
|
- debug build
|
|
All AutoTests Execution time : 519138ms
|
|
All AutoTests Average Execution time : 5191ms
|
|
- Qt 6.2.2 ; GCC 11.2 x64 ccache
|
|
- debug build
|
|
All AutoTests Execution time : 546585ms
|
|
All AutoTests Average Execution time : 5466ms
|