Update GPL blurb in source files.
[toast/confclerk.git] / src / orm / ormrecord.h
1 /*
2  * Copyright (C) 2010 Ixonos Plc.
3  *
4  * This file is part of ConfClerk.
5  *
6  * ConfClerk is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation, either version 2 of the License, or (at your option)
9  * any later version.
10  *
11  * ConfClerk is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * ConfClerk.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #ifndef ORMRECORD_H
20 #define ORMRECORD_H
21
22 #include <QSqlQuery>
23 #include <QSqlRecord>
24 #include <QSqlField>
25 #include <QSqlError>
26 #include <QStringList>
27 #include <QDateTime>
28 #include <QDebug>
29
30 class OrmException
31 {
32 public:
33     OrmException(const QString& text) : mText(text) {}
34     virtual ~OrmException(){}
35     virtual const QString& text() const { return mText; }
36 private:
37     QString mText;
38 };
39
40 class OrmNoObjectException : public OrmException
41 {
42 public:
43     OrmNoObjectException() : OrmException("No object exception"){}
44     ~OrmNoObjectException(){}
45 };
46
47 class OrmSqlException : public OrmException
48 {
49 public:
50     OrmSqlException(const QString& text) : OrmException( QString("Sql error: ") + text ) {}
51     ~OrmSqlException(){}
52 };
53
54 template <typename T>
55 class OrmRecord : protected QSqlRecord
56 {
57 public:
58     OrmRecord();
59     static T hydrate(const QSqlRecord& record);
60     void update(QString col, QVariant value = QVariant()); // updates specified column 'col'
61
62 protected:
63     QVariant value(QString col) const;
64     void setValue(QString col, QVariant value);
65
66     static T loadOne(QSqlQuery query);
67     static QList<T> load(QSqlQuery query);
68
69     // auxiliary methods
70     static QSqlRecord toRecord(const QList<QSqlField> & columnList);
71     // all record items/columns are in one table
72     static QString columnsForSelect(const QString& prefix = QString());
73     static QString selectQuery();
74     static QString updateQuery();
75
76     static QVariant convertToC(QVariant value, QVariant::Type colType);
77     static QVariant convertToDb(QVariant value, QVariant::Type colType);
78 };
79
80 template <typename T>
81 OrmRecord<T>::OrmRecord()
82 {
83     QSqlRecord::operator=(T::sColumns);
84 }
85
86 template <typename T>
87 T OrmRecord<T>::hydrate(const QSqlRecord& record)
88 {
89     T object;
90     object.QSqlRecord::operator=(record);
91     return object;
92 }
93
94 // updates specified column 'col'
95 // if the value is not specified  as an argument,
96 // it's taken from the reford itself
97 // see also: setValue() method for more details
98 template <typename T>
99 void OrmRecord<T>::update(QString col, QVariant value)
100 {
101     QSqlQuery query;
102     query.prepare(QString(updateQuery() + "SET %1 = :col WHERE id = :id").arg(col));
103     if(value.isValid()) // take 'col' value from the method's arguments
104         query.bindValue(":col", value);
105     else // take 'col' value from the record; see setValue()
106         query.bindValue(":col", convertToDb(this->value(col), this->value(col).type()));
107     query.bindValue(":id", this->value("id"));
108     //query.bindValue(":id", convertToDb(value("id"), QVariant::Int));
109     query.exec();
110 }
111
112 template <typename T>
113 QVariant OrmRecord<T>::value(QString col) const
114 {
115     return convertToC(QSqlRecord::value(col), T::sColumns.field(col).type());
116 }
117
118 template <typename T>
119 void OrmRecord<T>::setValue(QString col, QVariant value)
120 {
121     QSqlRecord::setValue(col, convertToDb(value, T::sColumns.field(col).type()));
122 }
123
124 template <typename T>
125 T OrmRecord<T>::loadOne(QSqlQuery query)
126 {
127     if (!query.isActive())
128     {
129         if (!query.exec())
130         {
131             throw OrmSqlException(query.lastError().text());
132         }
133     }
134
135     if (!query.next())
136     {
137         throw OrmNoObjectException();
138     }
139
140     return hydrate(query.record());
141 }
142
143 template <typename T>
144 QList<T> OrmRecord<T>::load(QSqlQuery query)
145 {
146     if (!query.isActive())
147     {
148         if (!query.exec())
149         {
150             qDebug() << "Error: " << query.lastError().driverText() << "; Type: " << query.lastError().type();
151             throw OrmSqlException(query.lastError().text());
152         }
153         else
154         {
155             /*qDebug() << "SQL OK";*/
156         }
157     }
158
159     QList<T> objects;
160     while (query.next())
161     {
162         objects << hydrate(query.record());
163     }
164     /*qDebug() << "Fetch done";*/
165     return objects;
166 }
167
168 template <typename T>
169 QString OrmRecord<T>::columnsForSelect(const QString& prefix)
170 {
171     QStringList prefixedColumns;
172     for (int i=0; i<T::sColumns.count(); i++)
173     {
174         prefixedColumns.append(prefix.isEmpty() ?
175             T::sColumns.field(i).name() :
176             QString("%1.%2").arg(prefix, T::sColumns.field(i).name()));
177     }
178     return prefixedColumns.join(",");
179 }
180
181 template <typename T>
182 QString OrmRecord<T>::selectQuery()
183 {
184     return QString("SELECT %1 FROM %2 ").arg(columnsForSelect(), T::sTableName);
185 }
186
187 template <typename T>
188 QString OrmRecord<T>::updateQuery()
189 {
190     return QString("UPDATE %1 ").arg(T::sTableName);
191 }
192
193 template <typename T>
194 QSqlRecord OrmRecord<T>::toRecord(const QList<QSqlField> & columnList)
195 {
196     QSqlRecord record;
197     for(int i=0; i< columnList.count(); i++)
198     {
199         record.append(columnList[i]);
200     }
201     return record;
202 }
203
204 template <typename T>
205 QVariant OrmRecord<T>::convertToC(QVariant value, QVariant::Type colType)
206 {
207     if (colType == QVariant::DateTime && value.canConvert<uint>())
208     {
209         QDateTime date;
210         date.setTimeSpec(Qt::UTC);
211         date.setTime_t(value.toUInt());
212         return date;
213     }
214
215     return value;
216 }
217
218 template <typename T>
219 QVariant OrmRecord<T>::convertToDb(QVariant value, QVariant::Type colType)
220 {
221     if (colType == QVariant::DateTime && value.canConvert<QDateTime>())
222     {
223         return value.toDateTime().toTime_t();
224     }
225
226     return value;
227 }
228
229 #endif // ORMRECORD_H
230