/**************************************************************************** ** ** https://www.qxorm.com/ ** Copyright (C) 2013 XDL Team (ic-east.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 XDL Team ** ** 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 : ** ic-east.com ** ****************************************************************************/ #ifndef _QX_DAO_SESSION_H_ #define _QX_DAO_SESSION_H_ #ifdef _MSC_VER #pragma once #endif /*! * \file QxSession.h * \author XDL Team * \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, bool bAutoRollbackWhenDestroyed = false); virtual ~QxSession(); bool isThrowable() const; bool isOpened() const; bool isValid() const; bool isAutoRollbackWhenDestroyed() const; void setAutoRollbackWhenDestroyed(bool b); 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(), bool bUseExecBatch = false) { QSqlError err; if (relation.count() == 0) { err = qx::dao::insert(t, this->database(), bUseExecBatch); } 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(), bool bUseExecBatch = false) { QSqlError err; if (relation.count() == 0) { err = qx::dao::update_by_query(query, t, this->database(), columns, bUseExecBatch); } 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, bool bUseExecBatch = false) { QSqlError err = qx::dao::delete_by_id(t, this->database(), bUseExecBatch); 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, bool bUseExecBatch = false) { QSqlError err = qx::dao::destroy_by_id(t, this->database(), bUseExecBatch); 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_