/**************************************************************************** ** ** https://www.qxorm.com/ ** Copyright (C) 2013 Lionel Marty (contact@qxorm.com) ** ** This file is part of the QxOrm library ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any ** damages arising from the use of this software ** ** Commercial Usage ** Licensees holding valid commercial QxOrm licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Lionel Marty ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file 'license.gpl3.txt' included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met : http://www.gnu.org/copyleft/gpl.html ** ** If you are unsure which license is appropriate for your use, or ** if you have questions regarding the use of this file, please contact : ** contact@qxorm.com ** ****************************************************************************/ #ifndef _QX_DAO_SESSION_H_ #define _QX_DAO_SESSION_H_ #ifdef _MSC_VER #pragma once #endif /*! * \file QxSession.h * \author Lionel Marty * \ingroup QxDao * \brief Define a session to manage automatically database transactions (using C++ RAII) */ #include #include #include #include #include #include #include #include #include #include namespace qx { /*! * \ingroup QxDao * \brief qx::QxSession : define a session to manage automatically database transactions (using C++ RAII) * * A database transaction is a sequence of operations performed as a single logical unit of work. * If no errors occurred during the execution of the transaction then the system commits the transaction. * If an error occurs during the transaction, or if the user specifies a rollback operation, the data manipulations within the transaction are not persisted to the database. * * The qx::QxSession class of QxOrm library is designed to manage automatically database transactions (using C++ RAII) : * \code { // Start a scope where a new session is instantiated // Create a session : a valid database connexion by thread is automatically assigned to the session and a transaction is opened qx::QxSession session; // Execute some operations with database (using += operator of qx::QxSession class and session database connexion) session += qx::dao::insert(my_object, session.database()); session += qx::dao::update(my_object, session.database()); session += qx::dao::fetch_by_id(my_object, session.database()); session += qx::dao::delete_by_id(my_object, session.database()); // If the session is not valid (so an error occured) => display first error if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); } } // End of scope : session is destroyed (transaction => automatically commit or rollback if there is an error) * \endcode * * Note : a session can throw a qx::dao::sql_error exception when a SQL error occured (by default, there is no exception). * You can setup this feature using : * - qx::QxSession constructor (for a specific session) ; * - qx::QxSqlDatabase::getSingleton()->setSessionThrowable(bool b) parameter (for all sessions). * * Other note : don't forget to pass the session database connexion to each qx::dao::xxx functions (using session.database() method). * Moreover, you can manage your own database connexion (from a connexion pool for example) using constructor of qx::QxSession class. * * qx::QxSession class provides also persistent methods (CRUD) to make easier to write C++ code. * Here is the same example using methods of qx::QxSession class instead of functions into namespace qx::dao : * \code { // Start a scope where a new session is instantiated // Create a session : a valid database connexion by thread is automatically assigned to the session and a transaction is opened qx::QxSession session; // Execute some operations with database session.insert(my_object); session.update(my_object); session.fetchById(my_object); session.deleteById(my_object); // If the session is not valid (so an error occured) => display first error if (! session.isValid()) { qDebug("[QxOrm] session error : '%s'", qPrintable(session.firstError().text())); } } // End of scope : session is destroyed (transaction => automatically commit or rollback if there is an error) * \endcode */ class QX_DLL_EXPORT QxSession { private: struct QxSessionImpl; std::shared_ptr m_pImpl; //!< Private implementation idiom (use std::shared_ptr instead of std::unique_ptr because of incomplete type) public: QxSession(); QxSession(const QSqlDatabase & database); QxSession(const QSqlDatabase & database, bool bOpenTransaction); QxSession(const QSqlDatabase & database, bool bOpenTransaction, bool bThrowable); virtual ~QxSession(); bool isThrowable() const; bool isOpened() const; bool isValid() const; QSqlError firstError() const; QSqlError lastError() const; QList allErrors() const; const QSqlDatabase * database() const; QSqlDatabase * database(); bool open(); bool close(); bool commit(); bool rollback(); QxSession & operator+= (const QSqlError & err); static QxSession * getActiveSession(QSqlDatabase * db); void ignoreSoftDelete(bool bIgnoreSoftDelete = true, const QStringList & classesToIgnore = QStringList()); bool checkIgnoreSoftDelete(const QString & classKey) const; QString getIgnoreSoftDeleteHash() const; template long count(const qx::QxSqlQuery & query = qx::QxSqlQuery()) { return qx::dao::count(query, this->database()); } template QSqlError count(long & lCount, const qx::QxSqlQuery & query = qx::QxSqlQuery()) { return qx::dao::count(lCount, query, this->database()); } template T * fetchById(const QVariant & id, const QStringList & columns = QStringList(), const QStringList & relation = QStringList()) { IxDataMemberX * pDataMemberX = QxClass::getSingleton()->getDataMemberX(); IxDataMember * pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); if (! pDataMemberId) { qAssert(false); return NULL; } T * t = new T(); QSqlError err; pDataMemberId->fromVariant(t, id, -1, qx::cvt::context::e_database); if (relation.count() == 0) { err = qx::dao::fetch_by_id((* t), this->database(), columns); } else { err = qx::dao::fetch_by_id_with_relation(relation, (* t), this->database()); } if (err.isValid()) { delete t; t = NULL; (* this) += err; } return t; } template QSqlError fetchById(T & t, const QStringList & columns = QStringList(), const QStringList & relation = QStringList()) { QSqlError err; if (relation.count() == 0) { err = qx::dao::fetch_by_id(t, this->database(), columns); } else { err = qx::dao::fetch_by_id_with_relation(relation, t, this->database()); } if (err.isValid()) { (* this) += err; } return err; } template QSqlError fetchAll(T & t, const QStringList & columns = QStringList(), const QStringList & relation = QStringList()) { QSqlError err; if (relation.count() == 0) { err = qx::dao::fetch_all(t, this->database(), columns); } else { err = qx::dao::fetch_all_with_relation(relation, t, this->database()); } if (err.isValid()) { (* this) += err; } return err; } template QSqlError fetchByQuery(const qx::QxSqlQuery & query, T & t, const QStringList & columns = QStringList(), const QStringList & relation = QStringList()) { QSqlError err; if (relation.count() == 0) { err = qx::dao::fetch_by_query(query, t, this->database(), columns); } else { err = qx::dao::fetch_by_query_with_relation(relation, query, t, this->database()); } if (err.isValid()) { (* this) += err; } return err; } template QSqlError insert(T & t, const QStringList & relation = QStringList()) { QSqlError err; if (relation.count() == 0) { err = qx::dao::insert(t, this->database()); } else { err = qx::dao::insert_with_relation(relation, t, this->database()); } if (err.isValid()) { (* this) += err; } return err; } template QSqlError update(T & t, const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & columns = QStringList(), const QStringList & relation = QStringList()) { QSqlError err; if (relation.count() == 0) { err = qx::dao::update_by_query(query, t, this->database(), columns); } else { err = qx::dao::update_by_query_with_relation(relation, query, t, this->database()); } if (err.isValid()) { (* this) += err; } return err; } template QSqlError save(T & t, const QStringList & relation = QStringList()) { QSqlError err; if (relation.count() == 0) { err = qx::dao::save(t, this->database()); } else { err = qx::dao::save_with_relation(relation, t, this->database()); } if (err.isValid()) { (* this) += err; } return err; } template QSqlError deleteById(const QVariant & id) { IxDataMemberX * pDataMemberX = QxClass::getSingleton()->getDataMemberX(); IxDataMember * pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); if (! pDataMemberId) { qAssert(false); return QSqlError(); } std::shared_ptr t = std::make_shared(); pDataMemberId->fromVariant(t.get(), id, -1, qx::cvt::context::e_database); QSqlError err = qx::dao::delete_by_id((* t), this->database()); if (err.isValid()) { (* this) += err; } return err; } template QSqlError deleteById(T & t) { QSqlError err = qx::dao::delete_by_id(t, this->database()); if (err.isValid()) { (* this) += err; } return err; } template QSqlError deleteAll() { QSqlError err = qx::dao::delete_all(this->database()); if (err.isValid()) { (* this) += err; } return err; } template QSqlError deleteByQuery(const qx::QxSqlQuery & query) { QSqlError err = qx::dao::delete_by_query(query, this->database()); if (err.isValid()) { (* this) += err; } return err; } template QSqlError destroyById(const QVariant & id) { IxDataMemberX * pDataMemberX = QxClass::getSingleton()->getDataMemberX(); IxDataMember * pDataMemberId = (pDataMemberX ? pDataMemberX->getId_WithDaoStrategy() : NULL); if (! pDataMemberId) { qAssert(false); return QSqlError(); } std::shared_ptr t = std::make_shared(); pDataMemberId->fromVariant(t.get(), id, -1, qx::cvt::context::e_database); QSqlError err = qx::dao::destroy_by_id((* t), this->database()); if (err.isValid()) { (* this) += err; } return err; } template QSqlError destroyById(T & t) { QSqlError err = qx::dao::destroy_by_id(t, this->database()); if (err.isValid()) { (* this) += err; } return err; } template QSqlError destroyAll() { QSqlError err = qx::dao::destroy_all(this->database()); if (err.isValid()) { (* this) += err; } return err; } template QSqlError destroyByQuery(const qx::QxSqlQuery & query) { QSqlError err = qx::dao::destroy_by_query(query, this->database()); if (err.isValid()) { (* this) += err; } return err; } template QSqlError executeQuery(qx::QxSqlQuery & query, T & t) { QSqlError err = qx::dao::execute_query(query, t, this->database()); if (err.isValid()) { (* this) += err; } return err; } QSqlError callQuery(qx::QxSqlQuery & query) { QSqlError err = qx::dao::call_query(query, this->database()); if (err.isValid()) { (* this) += err; } return err; } template qx_bool exist(T & t) { return qx::dao::exist(t, this->database()); } private: QxSession(const QxSession & other) { Q_UNUSED(other); } QxSession & operator=(const QxSession & other) { Q_UNUSED(other); return (* this); } }; } // namespace qx #endif // _QX_DAO_SESSION_H_