commit 0d155b404741a18784cbc850f45bd49908f2e990
Author: Christian Ehringfeld <c.ehringfeld@t-online.de>
Date:   Fri Aug 28 00:18:01 2015 +0200

    bugfix in schema and ticket #580

diff --git a/src/cache.cpp b/src/cache.cpp
index 0f5d469..59f860c 100644
--- a/src/cache.cpp
+++ b/src/cache.cpp
@@ -72,7 +72,7 @@ QSharedPointer<Entity> Cache::get(qint64 id, const QString &classname) {
     return QSharedPointer<Entity>();
 }
 
-QString Cache::generateKey(qint64 id, const QString &classname) {
+QString Cache::generateKey(qint64 id, const QString &classname) const {
     if (id > -1) {
         return QString::number(id).append("[").append(classname).append("]");
     }
diff --git a/src/cache.h b/src/cache.h
index 8696fde..a6bef82 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -61,7 +61,7 @@ class Cache {
     }
 
   protected:
-    QString generateKey(qint64 id, const QString &classname);
+    QString generateKey(qint64 id, const QString &classname) const;
   private:
     QHash<QString, QWeakPointer<Entity>> cache;
 };
diff --git a/src/entitymanager.cpp b/src/entitymanager.cpp
index 1b1925e..dcb707f 100644
--- a/src/entitymanager.cpp
+++ b/src/entitymanager.cpp
@@ -21,6 +21,7 @@
 #include "validators/validatorfactory.h"
 #include "validators/validator.h"
 #include "validators/validatorrule.h"
+#include <QHash>
 using namespace CuteEntityManager;
 
 QStringList EntityManager::connectionNames = QStringList();
@@ -36,6 +37,121 @@ QSharedPointer<QueryBuilder> EntityManager::getQueryBuilder() const {
     return this->schema->getQueryBuilder();
 }
 
+bool EntityManager::saveObject(QSharedPointer<Entity> &entity,
+                               QList<Entity *> &mergedObjects, const bool persistRelations,
+                               const bool ignoreHasChanged, const bool validate) {
+    bool merged =  mergedObjects.contains(entity.data());
+    if (ignoreHasChanged || (!ignoreHasChanged && !merged
+                             && this->hasChanged(entity))) {
+        if (entity->getProperty(entity->getPrimaryKey()).toLongLong() > -1) {
+            return this->mergeObject(entity, mergedObjects, persistRelations, validate);
+        } else {
+            return this->createObject(entity, mergedObjects, persistRelations, false,
+                                      validate);
+        }
+    }
+    return merged ? true : false;
+}
+
+bool EntityManager::mergeObject(QSharedPointer<Entity> &entity,
+                                QList<Entity *> &mergedObjects, bool withRelations, const bool validate) {
+    bool ok = true;
+    if (!mergedObjects.contains(entity.data())) {
+        mergedObjects.append(entity.data());
+        ok = false;
+        if (entity->getId() > -1 && ((validate && this->validate(entity))
+                                     || !validate)) {
+            if (withRelations) {
+                this->savePrePersistedRelations(entity, mergedObjects);
+            }
+            this->db->startTransaction();
+            QList<QSqlQuery> q = this->schema->getQueryBuilder()->merge(
+                                     entity);
+            ok = this->db->exec(q);
+
+            if (!ok || !this->db->commitTransaction()) {
+                this->db->rollbackTransaction();
+                ok = false;
+            } else if (ok && withRelations) {
+                this->savePostPersistedRelations(entity, mergedObjects);
+            }
+        }
+    }
+    return ok;
+}
+
+bool EntityManager::createObject(QSharedPointer<Entity> &entity,
+                                 QList<Entity *> &mergedObjects, const bool persistRelations,
+                                 const bool checkDuplicate, const bool validate) {
+    bool rc = true;
+    if (!mergedObjects.contains(entity.data())) {
+        mergedObjects.append(entity.data());
+        rc = false;
+        if (this->checkTable(entity) && ((validate && this->validate(entity))
+                                         || !validate) && !(checkDuplicate && this->count(entity) > 0)) {
+            if (persistRelations) {
+                this->savePrePersistedRelations(entity, mergedObjects);
+            }
+            this->db->startTransaction();
+            QList<QSqlQuery> q = this->schema->getQueryBuilder()->create(
+                                     entity);
+            bool first = true;
+            QVariant id = -1;
+            for (int var = 0; var < q.size(); ++var) {
+                auto query = q.at(var);
+                if (!first) {
+                    this->schema->getQueryBuilder()->bindValue(entity->getPrimaryKey(), id, query);
+                }
+                rc = this->db->exec(query);
+                if (!rc) {
+                    qWarning() << "class is erroneous:" <<  EntityHelper::getClassname(
+                                   entity.data());
+                    break;
+                }
+                if (first) {
+                    id = query.lastInsertId();
+                    entity->setProperty(entity->getPrimaryKey(), id);
+                    first = false;
+                }
+            }
+            if (!rc || !this->db->commitTransaction()) {
+                this->db->rollbackTransaction();
+                entity->setId(-1);
+                rc = false;
+            } else {
+                this->cache.insert(entity);
+                if (persistRelations) {
+                    this->savePostPersistedRelations(entity, mergedObjects);
+                }
+                rc = true;
+            }
+        }
+        entity->idChanged();
+    }
+    return rc;
+}
+
+
+bool EntityManager::merge(QSharedPointer<Entity> &entity, bool withRelations,
+                          const bool validate) {
+    auto merged = QList<Entity *>();
+    return this->mergeObject(entity, merged, withRelations, validate);
+}
+
+bool EntityManager::create(QSharedPointer<Entity> &entity,
+                           const bool persistRelations, const bool checkDuplicate, const bool validate) {
+    auto merged = QList<Entity *>();
+    return this->createObject(entity, merged, persistRelations,
+                              checkDuplicate, validate);
+}
+
+bool EntityManager::save(QSharedPointer<Entity> &entity,
+                         const bool persistRelations, const bool ignoreHasChanged, const bool validate) {
+    auto merged = QList<Entity *>();
+    return this->saveObject(entity, merged, persistRelations,
+                            ignoreHasChanged, validate);
+}
+
 EntityManager::EntityManager(QSqlDatabase database,
                              bool logQueries) : QObject() {
     auto db = new Database(database, true, logQueries);
@@ -341,7 +457,7 @@ bool EntityManager::canPersistRelation(const Relation &relation,
 }
 
 void EntityManager::savePrePersistedRelations(const QSharedPointer<Entity>
-        &entity) {
+        &entity, QList<Entity *> &mergedObjects) {
     auto relations = EntityHelper::getRelationProperties(entity.data());
     auto iterator = relations.constBegin();
     while (iterator != relations.constEnd()) {
@@ -351,7 +467,7 @@ void EntityManager::savePrePersistedRelations(const QSharedPointer<Entity>
             if (r.getType() == RelationType::MANY_TO_ONE) {
                 auto e = EntityInstanceFactory::castQVariant(var);
                 if (this->shouldBeSaved(e, r)) {
-                    this->save(e, true, false);
+                    this->saveObject(e, mergedObjects);
                     auto fkProp = EntityHelper::mappedProperty(r, e);
                     if (fkProp.isValid()) {
                         EntityHelper::addEntityToListProperty(e, entity, fkProp);
@@ -360,7 +476,7 @@ void EntityManager::savePrePersistedRelations(const QSharedPointer<Entity>
             } else if (r.getType() == RelationType::ONE_TO_ONE
                        && r.getMappedBy().isEmpty()) {
                 auto e =  EntityInstanceFactory::castQVariant(var);
-                this->save(e, true, false);
+                this->saveObject(e, mergedObjects);
                 auto prop = EntityHelper::mappedProperty(r, e);
                 if (prop.isValid()) {
                     EntityHelper::setProperty(e, entity, prop);
@@ -372,7 +488,7 @@ void EntityManager::savePrePersistedRelations(const QSharedPointer<Entity>
 }
 
 void EntityManager::savePostPersistedRelations(const QSharedPointer<Entity>
-        &entity) {
+        &entity, QList<Entity *> &mergedObjects) {
     auto relations = EntityHelper::getRelationProperties(entity.data());
     auto iterator = relations.constBegin();
     while (iterator != relations.constEnd()) {
@@ -380,7 +496,7 @@ void EntityManager::savePostPersistedRelations(const QSharedPointer<Entity>
         auto var = iterator.value().read(entity.data());
         if (!var.isNull()) {
             if (this->canPersistRelation(r, RelationType::MANY_TO_MANY, var)) {
-                this->persistManyToMany(entity, r, var);
+                this->persistManyToMany(entity, r, var, mergedObjects);
             } else if (this->canPersistRelation(r, RelationType::ONE_TO_MANY, var)) {
                 QList<QSharedPointer<Entity>> list = EntityInstanceFactory::castQVariantList(
                         var);
@@ -389,7 +505,7 @@ void EntityManager::savePostPersistedRelations(const QSharedPointer<Entity>
                     for (int var = 0; var < list.size(); ++var) {
                         auto e = list.at(var);
                         if (this->shouldBeSaved(e, r)) {
-                            this->save(e, true, false);
+                            this->saveObject(e, mergedObjects);
                             if (fkProp.isValid()) {
                                 EntityHelper::addEntityToListProperty(e, entity, fkProp);
                             }
@@ -399,7 +515,7 @@ void EntityManager::savePostPersistedRelations(const QSharedPointer<Entity>
             } else if (r.getType() == RelationType::ONE_TO_ONE
                        && !r.getMappedBy().isEmpty()) {
                 auto e =  EntityInstanceFactory::castQVariant(var);
-                this->save(e, true, false);
+                this->saveObject(e, mergedObjects);
                 auto fkProp = EntityHelper::mappedProperty(r, e);
                 if (fkProp.isValid()) {
                     EntityHelper::addEntityToListProperty(e, entity, fkProp);
@@ -413,13 +529,13 @@ void EntityManager::savePostPersistedRelations(const QSharedPointer<Entity>
 void EntityManager::persistMappedByRelation(const QList<QSharedPointer<Entity> >
         &list, QSqlQuery &q, const QSharedPointer<Entity> &entity,
         const QSharedPointer<Entity> &ptr, const Relation &r,
-        const QString &tblName) {
+        const QString &tblName, QList<Entity *> &mergedObjects) {
     q.clear();
     QList<QSharedPointer<Entity>> saved =
                                    r.getCascadeType().contains(CascadeType::ALL) ||
                                    r.getCascadeType().contains(CascadeType::MERGE) ||
                                    r.getCascadeType().contains(CascadeType::PERSIST) ?
-                                   this->saveRelationEntities(list, r) : list;
+                                   this->saveRelationEntities(list, r, mergedObjects) : list;
     this->db->startTransaction();
     auto builder = this->schema->getQueryBuilder();
     q = builder->manyToManyInsert(tblName,
@@ -474,6 +590,7 @@ void EntityManager::appendToInstanceList() {
     this->setObjectName(this->generateObjectName());
     EntityManager::instances.insert(this->objectName(), this);
 }
+
 QHash<QString, EntityManager *> EntityManager::getInstances() {
     return instances;
 }
@@ -613,12 +730,13 @@ void EntityManager::removeManyToManyEntityList(const QSharedPointer<Entity> &e,
 }
 
 QList<QSharedPointer<Entity>> EntityManager::saveRelationEntities(
-const QList<QSharedPointer<Entity> > &list, const Relation &r) {
+                               const QList<QSharedPointer<Entity> > &list, const Relation &r,
+QList<Entity *> &mergedObjects) {
     QList<QSharedPointer<Entity>> saved = QList<QSharedPointer<Entity>>();
     QSharedPointer<Entity> ptr;
     for (int var = 0; var < list.size(); ++var) {
         ptr = list.at(var);
-        if ((this->shouldBeSaved(ptr, r) && this->save(ptr, true, false))
+        if ((this->shouldBeSaved(ptr, r) && this->saveObject(ptr, mergedObjects))
                 || ptr->getProperty(ptr->getPrimaryKey()).toLongLong() > -1) {
             saved.append(ptr);
         }
@@ -627,20 +745,20 @@ const QList<QSharedPointer<Entity> > &list, const Relation &r) {
 }
 
 void EntityManager::persistManyToMany(const QSharedPointer<Entity> &entity,
-                                      const Relation &r, QVariant &property) {
+                                      const Relation &r, QVariant &property, QList<Entity *> &mergedObjects) {
     auto list = property.value<QList<QVariant>>();
     if (!list.isEmpty() && !(list.at(0).isNull())) {
         auto var = list.at(0);
         auto ptr = EntityInstanceFactory::castQVariant(var);
         auto builder = this->schema->getQueryBuilder();
         QString tblName = builder->generateManyToManyTableName(entity, ptr, r);
-        if (this->schema->getTables().contains(tblName)) {
+        if (this->schema->containsTable(tblName)) {
             QSqlQuery q = builder->manyToManyDelete(
                               tblName, builder->generateManyToManyColumnName(entity),
                               entity->getProperty(entity->getPrimaryKey()).toLongLong());
             if (this->db->exec(q)) {
                 auto nList = EntityInstanceFactory::castQVariantList(property);
-                this->persistMappedByRelation(nList, q, entity, ptr, r, tblName);
+                this->persistMappedByRelation(nList, q, entity, ptr, r, tblName, mergedObjects);
             }
         } else {
             this->missingManyToManyTable(tblName, entity, r);
@@ -725,74 +843,6 @@ bool EntityManager::create(QList<QSharedPointer<Entity> > entities,
     return ok;
 }
 
-bool EntityManager::create(QSharedPointer<Entity> &entity,
-                           const bool persistRelations, const bool checkDuplicate, const bool validate) {
-    bool rc = false;
-    if (this->checkTable(entity) && ((validate && this->validate(entity))
-                                     || !validate) && !(checkDuplicate && this->count(entity) > 0)) {
-        if (persistRelations) {
-            this->savePrePersistedRelations(entity);
-        }
-        this->db->startTransaction();
-        QList<QSqlQuery> q = this->schema->getQueryBuilder()->create(
-                                 entity);
-        bool first = true;
-        QVariant id = -1;
-        for (int var = 0; var < q.size(); ++var) {
-            auto query = q.at(var);
-            if (!first) {
-                this->schema->getQueryBuilder()->bindValue(entity->getPrimaryKey(), id, query);
-            }
-            rc = this->db->exec(query);
-            if (!rc) {
-                qWarning() << "class is erroneous:" <<  EntityHelper::getClassname(
-                               entity.data());
-                break;
-            }
-            if (first) {
-                id = query.lastInsertId();
-                entity->setProperty(entity->getPrimaryKey(), id);
-                first = false;
-            }
-        }
-        if (!rc || !this->db->commitTransaction()) {
-            this->db->rollbackTransaction();
-            entity->setId(-1);
-            rc = false;
-        } else {
-            this->cache.insert(entity);
-            if (persistRelations) {
-                this->savePostPersistedRelations(entity);
-            }
-            rc = true;
-        }
-    }
-    entity->idChanged();
-    return rc;
-}
-
-bool EntityManager::merge(QSharedPointer<Entity> &entity, bool withRelations,
-                          const bool validate) {
-    bool ok = false;
-    if (entity->getId() > -1 && ((validate && this->validate(entity))
-                                 || !validate)) {
-        if (withRelations) {
-            this->savePrePersistedRelations(entity);
-        }
-        this->db->startTransaction();
-        QList<QSqlQuery> q = this->schema->getQueryBuilder()->merge(
-                                 entity);
-        ok = this->db->exec(q);
-        if (!ok || !this->db->commitTransaction()) {
-            this->db->rollbackTransaction();
-            return false;
-        } else if (ok && withRelations) {
-            this->savePostPersistedRelations(entity);
-        }
-    }
-    return ok;
-}
-
 QHash<QString, QVariant> EntityManager::findByPk(qint64 id,
         const QSharedPointer<Entity>
         &e) {
@@ -877,18 +927,6 @@ void EntityManager::resolveRelations(const QSharedPointer<Entity> &entity,
     }
 }
 
-bool EntityManager::save(QSharedPointer<Entity> &entity,
-                         const bool persistRelations, const bool ignoreHasChanged) {
-    if (ignoreHasChanged || (!ignoreHasChanged && this->hasChanged(entity))) {
-        if (entity->getProperty(entity->getPrimaryKey()).toLongLong() > -1) {
-            return this->merge(entity, persistRelations);
-        } else {
-            return this->create(entity, persistRelations);
-        }
-    }
-    return false;
-}
-
 qint64 EntityManager::findId(QSharedPointer<Entity> &entity) {
     qint64 r = -1;
     QSqlQuery q = this->schema->getQueryBuilder()->findId(entity);
diff --git a/src/entitymanager.h b/src/entitymanager.h
index 02b1c8d..e37fff8 100644
--- a/src/entitymanager.h
+++ b/src/entitymanager.h
@@ -54,13 +54,13 @@ class EntityManager : public QObject {
     QList<QSharedPointer<Entity>> findEntityByAttributes(const
                                QSharedPointer<Entity> &entity,
                                bool ignoreID = false);
+    qint64 findId(QSharedPointer<Entity> &entity);
     bool create(QList<QSharedPointer<Entity>> entities,
                 const bool persistRelations = true, const bool validate = true);
     bool create(QSharedPointer<Entity> &entity, const bool persistRelations = true,
                 const bool checkDuplicate = false, const bool validate = true);
     bool save(QSharedPointer<Entity> &entity, const bool persistRelations = true,
-              const bool ignoreHasChanged = true);
-    qint64 findId(QSharedPointer<Entity> &entity);
+              const bool ignoreHasChanged = true, const bool validate = true);
     bool merge(QSharedPointer<Entity> &entity, bool withRelations = true,
                const bool validate = true);
     bool remove(QSharedPointer<Entity> &entity);
@@ -209,6 +209,12 @@ class EntityManager : public QObject {
     }
 
   protected:
+    bool saveObject(QSharedPointer<Entity> &entity, QList<Entity*> &mergedObjects, const bool persistRelations=true,
+              const bool ignoreHasChanged=true, const bool validate=true);
+    bool mergeObject(QSharedPointer<Entity> &entity, QList<Entity*> &mergedObjects, bool withRelations,
+               const bool validate);
+    bool createObject(QSharedPointer<Entity> &entity, QList<Entity*> &mergedObjects,const bool persistRelations,
+                const bool checkDuplicate, const bool validate);
     template<class T> QList<QSharedPointer<T>> convertList(const
     QList<QSharedPointer<Entity>> &list) {
         QList<QSharedPointer<T>> newList = QList<QSharedPointer<T>>();
@@ -236,7 +242,7 @@ class EntityManager : public QObject {
     bool canPersistRelation(const Relation &relation, const RelationType &r,
                             const QVariant &var) const;
     void persistManyToMany(const QSharedPointer<Entity> &entity, const Relation &r,
-                           QVariant &property);
+                           QVariant &property, QList<Entity*> &mergedObjects);
     QList<QHash<QString, QVariant> > findAllByAttributes(const
             QSharedPointer<Entity> &entity,
             bool ignoreID = false);
@@ -246,15 +252,15 @@ class EntityManager : public QObject {
             bool ignoreID = false);
     QSharedPointer<Entity> findById(const qint64 &id, QSharedPointer<Entity> &e,
                                     const bool refresh = false);
-    void savePrePersistedRelations(const QSharedPointer<Entity> &entity);
-    void savePostPersistedRelations(const QSharedPointer<Entity> &entity);
+    void savePrePersistedRelations(const QSharedPointer<Entity> &entity, QList<Entity*> &mergedObjects);
+    void savePostPersistedRelations(const QSharedPointer<Entity> &entity, QList<Entity*> &mergedObjects);
 
     QList<QSharedPointer<Entity>> saveRelationEntities(const
-                               QList<QSharedPointer<Entity>> &list, const Relation &r);
+                               QList<QSharedPointer<Entity>> &list, const Relation &r, QList<Entity*> &mergedObjects);
     void persistMappedByRelation(const QList<QSharedPointer<Entity>> &list,
                                  QSqlQuery &q, const QSharedPointer<Entity> &entity,
                                  const QSharedPointer<Entity> &ptr, const Relation &r,
-                                 const QString &tblName);
+                                 const QString &tblName, QList<Entity*> &mergedObjects);
     bool shouldBeSaved(QSharedPointer<Entity> &entity , const Relation &r);
     void removeRelations(const QSharedPointer<Entity> &entity);
     void removeEntityList(QVariant &var);
diff --git a/src/schema.cpp b/src/schema.cpp
index de7abac..e52706e 100644
--- a/src/schema.cpp
+++ b/src/schema.cpp
@@ -231,8 +231,7 @@ QString Schema::getRawTable(QString name) {
 }
 
 bool Schema::containsTable(QString tblname) {
-    if (this->tables.size() !=
-            this->database->getDatabase().tables().size()) {
+    if (this->tables.size() != this->getTableNames().size()) {
         this->setTables(this->getTableSchemas());
     }
     return this->database->getDatabase().tables().contains(tblname);
@@ -334,7 +333,10 @@ QString Schema::combineScaleAndPrecision(int precision, int scale) const {
 }
 
 
-QHash<QString, QSharedPointer<TableSchema> > Schema::getTables() const {
+QHash<QString, QSharedPointer<TableSchema> > Schema::getTables() {
+    if (this->tables.size() != this->getTableNames().size()) {
+        this->setTables(this->getTableSchemas());
+    }
     return this->tables;
 }
 
diff --git a/src/schema.h b/src/schema.h
index db3e304..70e0668 100644
--- a/src/schema.h
+++ b/src/schema.h
@@ -107,7 +107,7 @@ class Schema {
     virtual QString getRawTable(QString name);
     virtual bool containsTable(QString tblname);
 
-    QHash<QString, QSharedPointer<TableSchema> > getTables() const;
+    QHash<QString, QSharedPointer<TableSchema> > getTables();
     void setTables(const QHash<QString, QSharedPointer<TableSchema> > &value);
 
     QSharedPointer<Database> getDatabase() const;
