1 /******************************************************* 2 Copyright (C) 2013,2014 Guan Lisheng (guanlisheng@gmail.com) 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 ********************************************************/ 18 19 #ifndef MODEL_H 20 #define MODEL_H 21 22 #ifdef _MSC_VER 23 #pragma warning (disable:4100) 24 #endif 25 26 #include <vector> 27 #include <map> 28 #include <algorithm> 29 #include <wx/datetime.h> 30 #include "singleton.h" 31 #include <wx/sharedptr.h> 32 #include <wx/log.h> 33 #include <wx/string.h> 34 #include "db/DB_Table.h" 35 36 class wxString; 37 class wxSQLite3Statement; 38 class wxSQLite3Database; 39 class wxSQLite3ResultSet; 40 41 typedef wxDateTime wxDate; 42 43 class ModelBase 44 { 45 public: ModelBase()46 ModelBase():db_(0) {}; ~ModelBase()47 virtual ~ModelBase() {}; 48 49 public: Savepoint()50 void Savepoint() 51 { 52 this->db_->Savepoint("MMEX"); 53 } ReleaseSavepoint()54 void ReleaseSavepoint() 55 { 56 this->db_->ReleaseSavepoint("MMEX"); 57 } Rollback()58 void Rollback() 59 { 60 this->db_->Rollback("MMEX"); 61 } 62 protected: to_date(const wxString & str_date)63 static wxDate to_date(const wxString& str_date) 64 { 65 static std::map<wxString, wxDate> cache; 66 const auto it = cache.find(str_date); 67 if (it != cache.end()) return it->second; 68 69 wxDate date; 70 date.ParseISODate(str_date); // the date in ISO 8601 format "YYYY-MM-DD". 71 cache.insert(std::make_pair(str_date, date)); 72 return date; 73 } 74 public: 75 virtual json::Object cache_to_json() const = 0; 76 virtual void show_statistics() const = 0; 77 protected: 78 wxSQLite3Database* db_; 79 }; 80 81 template<class DB_TABLE> 82 class Model: public ModelBase, public DB_TABLE 83 { 84 public: 85 using DB_TABLE::all; 86 using DB_TABLE::get; 87 using DB_TABLE::save; 88 using DB_TABLE::remove; 89 90 typedef typename DB_TABLE::COLUMN COLUMN; 91 /** Return a list of Data record addresses (Data_Set) derived directly from the database. */ 92 const typename DB_TABLE::Data_Set all(COLUMN col = COLUMN(0), bool asc = true) 93 { 94 this->ensure(this->db_); 95 return all(db_, col, asc); 96 } 97 98 template<typename... Args> 99 /** 100 Command: find(const Args&... args) 101 Args: One or more Specialised Parameters creating SQL statement conditions used after the WHERE statement. 102 Specialised Parameters: Table_Column_Name(content)[, Table_Column_Name(content)[, ...]] 103 Example: 104 Model_Asset::ASSETID(2), Model_Asset::ASSETTYPE(Model_Asset::TYPE_JEWELLERY) 105 produces SQL statement condition: ASSETID = 2 AND ASSETTYPE = "Jewellery" 106 * Returns a Data_Set containing the addresses of the items found. 107 * The Data_Set is empty when nothing found. 108 */ find(const Args &...args)109 const typename DB_TABLE::Data_Set find(const Args&... args) 110 { 111 return find_by(this, db_, true, args...); 112 } 113 114 template<typename... Args> 115 /** 116 Command: find_or(const Args&... args) 117 Args: One or more Specialised Parameters creating SQL statement conditions used after the WHERE statement. 118 Specialised Parameters: Table_Column_Name(content)[, Table_Column_Name(content)[, ...]] 119 Example: 120 Model_Asset::ASSETID(2), Model_Asset::ASSETTYPE(Model_Asset::TYPE_JEWELLERY) 121 produces SQL statement condition: ASSETID = 2 OR ASSETTYPE = "Jewellery" 122 * Returns a Data_Set containing the addresses of the items found. 123 * The Data_Set is empty when nothing found. 124 */ find_or(const Args &...args)125 const typename DB_TABLE::Data_Set find_or(const Args&... args) 126 { 127 return find_by(this, db_, false, args...); 128 } 129 130 /** 131 * Return the Data record pointer for the given ID 132 * from either memory cache or the database. 133 */ get(int id)134 typename DB_TABLE::Data* get(int id) 135 { 136 return this->get(id, this->db_); 137 } 138 139 /** Save the Data record memory instance to the database. */ save(typename DB_TABLE::Data * r)140 int save(typename DB_TABLE::Data* r) 141 { 142 r->save(this->db_); 143 return r->id(); 144 } 145 146 /** 147 * Save all Data record memory instances contained 148 * in the record list (Data_Set) to the database. 149 */ 150 template<class DATA> save(std::vector<DATA> & rows)151 int save(std::vector<DATA>& rows) 152 { 153 this->Savepoint(); 154 for (auto& r : rows) 155 { 156 if (r.id() < 0) 157 wxLogDebug("Incorrect function call to save %s", r.to_json().c_str()); 158 this->save(&r); 159 } 160 this->ReleaseSavepoint(); 161 162 return rows.size(); 163 } 164 165 template<class DATA> save(std::vector<DATA * > & rows)166 int save(std::vector<DATA*>& rows) 167 { 168 this->Savepoint(); 169 for (auto& r : rows) this->save(r); 170 this->ReleaseSavepoint(); 171 172 return rows.size(); 173 } 174 175 /** Remove the Data record instance from memory and the database. */ remove(int id)176 bool remove(int id) 177 { 178 return this->remove(id, db_); 179 } 180 181 public: 182 void preload(int max_num = 1000) 183 { 184 int i = 0; 185 for (const auto & item : all()) 186 { 187 get(item.id()); 188 if (++i >= max_num) break; 189 } 190 } 191 cache_to_json()192 json::Object cache_to_json() const 193 { 194 json::Object o; 195 o[L"table"] = json::String(this->name().ToStdWstring()); 196 o[L"cached"] = json::Number(this->cache_.size()); 197 o[L"index_by_id"] = json::Number(this->index_by_id_.size()); 198 o[L"hit"] = json::Number(this->hit_); 199 o[L"miss"] = json::Number(this->miss_); 200 o[L"skip"] = json::Number(this->skip_); 201 202 return o; 203 } 204 /** Show table statistics*/ show_statistics()205 void show_statistics() const 206 { 207 size_t cache_size = this->cache_.size(); 208 size_t index_by_id_size = this->index_by_id_.size(); 209 #ifdef _WIN64 210 wxLogDebug("%s : (cache %llu, index_by_id %llu, hit %llu, miss %llu, skip %llu)", this->name(), cache_size, index_by_id_size, this->hit_, this->miss_, this->skip_); 211 #else 212 wxLogDebug("%s : (cache %lu, index_by_id %lu, hit %lu, miss %lu, skip %lu)", this->name(), cache_size, index_by_id_size, this->hit_, this->miss_, this->skip_); 213 #endif 214 } 215 }; 216 217 #endif // 218