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