/*================================================================= Copyright (C) 2014,2016 BizStation Corp All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. =================================================================*/ #include #include #include #include #include /** @brief Read records and manual O/R mapping example This program read records of the "user" table where group = 3. And O/R mapping to the user class. This program use the filter operation on server side. Please execute "create database" , "change schema" and "insert records" example before execute this example. */ using namespace bzs::db::protocol::tdap::client; using namespace bzs::db::protocol::tdap; class user; typedef boost::shared_ptr user_ptr; typedef std::vector user_ptr_list; typedef std::vector user_list; typedef boost::shared_ptr user_list_ptr; class group; typedef boost::shared_ptr group_ptr; typedef std::vector group_ptr_list; typedef std::vector group_list; typedef boost::shared_ptr group_list_ptr; /** class group This is a model */ class group { int m_id; std::string m_name; mutable user_ptr_list* m_users; protected: group(void* owner) : m_id(0), m_users(NULL) {} public: ~group() { delete m_users; } int id() const { return m_id; } const std::string& name() const { return m_name; } void setId(int v) { m_id = v; } void setName(const char* v) { m_name = v; } user_ptr_list& users() const { if (!m_users) m_users = new user_ptr_list; return *m_users; } /* create instance */ static group* create(void* owner) { return new group(owner); }; }; group* create(group_ptr_list& m, int) { return group::create(&m); } /** class group_fdi This is a dynamic field index cache resolv from field name. An index is cached at instruction execution time, it becomes strong to change of a schema. */ class group_fdi { public: short id; short name; /* Implement the init function which a argument of 'table*'. */ void init(table* tb) { id = tb->fieldNumByName(_T("id")); name = tb->fieldNumByName(_T("name")); } }; /* Global functions of fdi instance and init */ group_fdi* createFdi(group_fdi*) { return new group_fdi(); } void destroyFdi(group_fdi* p) { delete p; } void initFdi(group_fdi* fdi, table* tb) { fdi->init(tb); } /* model table mapping class */ class group_orm { const group_fdi& m_fdi; public: /* Define mdl_typename type */ typedef group mdl_typename; /* Define fdi_typename type When fdi is not used , It is define "typedef fdibase fdi_typename;" When other fdi is used, you can the fdi type specify at create activeObject instance like "activeObject" */ typedef group_fdi fdi_typename; /* Constructor , which a argument of 'fdi_typename fdi&' */ group_orm(const fdi_typename& fdi) : m_fdi(fdi) {} /* Compare function , only fields of unique keys are compared. */ bool compKeyValue(group& l, group& r, int keyNum) const { return l.id() < r.id(); } void setKeyValues(group& g, const fields& fds, int keyNum) { fds[m_fdi.id] = g.id(); } void writeMap(group& g, const fields& fds, int optipn) { fds[m_fdi.id] = g.id(); fds[m_fdi.name] = g.name(); } void readMap(group& g, const fields& fds, int optipn) { g.setId(fds[m_fdi.id].i()); g.setName(fds[m_fdi.name].a_str()); // get by ansi string } void readAuntoincValue(group& g, const fields& fds, int optipn) { g.setId(fds[m_fdi.id].i()); } /* Get a table name from a target model. */ const _TCHAR* getTableName() { return _T("group1"); } }; /** User class */ class user { mutable group* m_grp; int m_id; int m_group_id; std::string m_name; std::string m_tel; protected: user(void* owner) : m_grp(NULL), m_id(0) {} public: ~user() { delete m_grp; } group* grp() const { if (m_grp == NULL) { m_grp = group::create(0); m_grp->setId(m_group_id); } return m_grp; } int id() const { return m_id; } int group_id() const { return m_group_id; } const std::string& name() const { return m_name; } const std::string& tel() const { return m_tel; } void setId(int v) { m_id = v; } void setGroup_id(int v) { m_group_id = v; } void setName(const char* v) { m_name = v; } void setTel(const char* v) { m_tel = v; } static user* create(void* owner) { return new user(owner); }; }; class user_fdi { public: short id; short name; short tel; short group_id; void init(table* tb) { id = tb->fieldNumByName(_T("id")); name = tb->fieldNumByName(_T("name")); tel = tb->fieldNumByName(_T("tel")); group_id = tb->fieldNumByName(_T("group")); } }; user_fdi* createFdi(user_fdi*) { return new user_fdi(); } void destroyFdi(user_fdi* p) { delete p; } void initFdi(user_fdi* fdi, table* tb) { fdi->init(tb); } //------------------------------------------------------------------------------ // The original collection exsample //------------------------------------------------------------------------------ /* When the original collection used it is not vector, iterator for the collection is required. */ class mdls; class mdlsIterator : public boost::iterator_facade { friend class boost::iterator_core_access; size_t m_index; mdls* m_mdls; user*& dereference() const; void increment(); void decrement(); void advance(size_t n); int distance_to(const mdlsIterator& r) const; bool equal(const mdlsIterator& r) const; public: mdlsIterator(mdls* m, size_t index = 0); }; /* This is a exsample of the origianl collection of user model. */ class mdls { mutable std::vector m_users; public: void clear() { m_users.clear(); } user*& item(size_t index) const { return m_users[index]; } user* add(user* u) { m_users.push_back(u); return u; } size_t size() { return m_users.size(); } user* operator[](unsigned int index) const { return item(index); } /* Define item_type type */ typedef user* item_type; /* Define iterator type */ typedef mdlsIterator iterator; }; /* implement th iterator */ user*& mdlsIterator::dereference() const { return m_mdls->item(m_index); } void mdlsIterator::increment() { ++m_index; } void mdlsIterator::decrement() { --m_index; } void mdlsIterator::advance(size_t n) { m_index += n; } int mdlsIterator::distance_to(const mdlsIterator& r) const { return (int)(r.m_index - m_index); } bool mdlsIterator::equal(const mdlsIterator& r) const { return m_index == r.m_index; } mdlsIterator::mdlsIterator(mdls* m, size_t index) : m_index(index), m_mdls(m) { } /* Implemant global functions of push_back begin end and clear in client * namespace */ namespace bzs { namespace db { namespace protocol { namespace tdap { namespace client { template <> inline mdlsIterator begin(mdls& m) { return mdlsIterator(&m, 0); } template <> inline mdlsIterator end(mdls& m) { return mdlsIterator(&m, m.size()); } #if (_MSC_VER || (__BCPLUSPLUS__ && !defined(__clang__))) inline void push_back(mdls& m, user* u) { m.add(u); } #else template <> inline void push_back(mdls& m, user* u) { m.add(u); } #endif } } } } } //------------------------------------------------------------------------------ user* create(mdls& m, int) { return user::create(&m); } user* create(user_ptr_list& m, int) { return user::create(&m); } class user_orm { const user_fdi& m_fdi; public: user_orm(const user_fdi& fdi) : m_fdi(fdi) {} bool compKeyValue(user& l, user& r, int keyNum) const { if (keyNum == 0) return l.id() < r.id(); return 1; } void setKeyValues(user& u, const fields& fds, int keyNum) { fds[m_fdi.id] = u.id(); } void writeMap(user& u, const fields& fds, int optipn) { fds[m_fdi.id] = u.id(); fds[m_fdi.name] = u.name(); fds[m_fdi.tel] = u.tel(); fds[m_fdi.group_id] = u.group_id(); } void readMap(user& u, const fields& fds, int optipn) { u.setId(fds[m_fdi.id].i()); u.setGroup_id(fds[m_fdi.group_id].i()); u.setName(fds[m_fdi.name].a_str()); // get by ansi string u.setTel(fds[m_fdi.tel].a_str()); // get by ansi string } void readAuntoincValue(user& u, const fields& fds, int optipn) { u.setId(fds[m_fdi.id].i()); } const _TCHAR* getTableName() { return _T("user"); } typedef user mdl_typename; typedef user_fdi fdi_typename; /* Definition the collection_orm_typename type when using an original * collection.*/ typedef mdlsHandler collection_orm_typename; }; typedef user_orm::collection_orm_typename users_orm; //------------------------------------------------------------------------------ /* client filter function */ int isMatch(const fields& fds) { return filter_validate_value; } /** dump user to screen */ template void dumpUser(const T user) { std::cout << " id " << user->id() << std::endl; std::cout << " name " << user->name() << std::endl; std::cout << " group " << user->grp()->name() << std::endl; std::cout << " tel " << user->tel() << std::endl << std::endl; } bool sortFunc2(const user* l, const user* r) { return l->name() < r->name(); } void readUsers(databaseManager& db, std::vector& users) { static int find_group_id = 2; static const char_td keynum_group = 1; static const char_td primary_key = 0; /* Create the activeObject instance of user_orm. */ activeObject ut(&db); //------------------------------------------------------------------------- // Single model operations //------------------------------------------------------------------------- /* Create user of id=12. */ int id = 12; user_ptr u(user::create(0)); u->setId(id); u->setName("moriwaki"); u->setTel("81-999-9999"); u->setGroup_id(1); /* Save a user */ ut.index(primary_key); ut.save(*u); // insert or update /* Read a user of id=12 */ ut.read(*u); /* Update telephone number of the user. */ u->setTel("81-999-8888"); ut.update(*u); /* Changing unique key value .*/ ut.index(primary_key).keyValue(u->id()); u->setId(13); bool noKeyValueFromObj = false; ut.update(*u, noKeyValueFromObj); /* Dont read key value from model */ /* Delete a user 1. */ ut.del(*u); /* Delete a user 2. */ // ut.index(primary_key).keyValue(id).del(); /* Read a group model. Using shared pointer. */ group_ptr grp(group::create(0)); activeObject gt(&db); gt.index(primary_key).keyValue(2).read(*grp, noKeyValueFromObj); /* Using pure pointer. */ group* g = group::create(0); g->setId(2); gt.read(*g); delete g; //------------------------------------------------------------------------- // Multi model operations (find operation) //------------------------------------------------------------------------- /* Set key number and key value of start record position */ ut.index(keynum_group).keyValue(find_group_id); /* Create query of find */ query q; q.select(_T("*")).where(_T("group"), _T("="), find_group_id); /* Execute read by the query. Users is vector collection . */ ut.read(users, q); /* Using original collection. */ mdls m; ut.index(keynum_group).keyValue(find_group_id).read(m, q); /* When Using other map handler, use readMap method. */ users_orm users_hdr(m); ut.readMap(users_hdr, q); /* Using client filter exsample. Set a client filter function or function object to read method. */ ut.index(keynum_group).keyValue(find_group_id).read(users, q, isMatch); /* Using id list reading, that like 'in' of SQL. Read groups of id= 1, 2, and 3 */ gt.index(primary_key); q.reset(); std::vector gmdls; q.select(_T("id"), _T("name")).in(1, 2, 3); gt.read(gmdls, q); //------------------------------------------------------------------------- // Like the ActiveRecord operation has_many //------------------------------------------------------------------------- q.reset().where(_T("group"), _T("="), grp->id()); ut.index(keynum_group).keyValue(grp->id()).read(grp->users(), q); //------------------------------------------------------------------------- // Like the ActiveRecord operations belongs_to and has_one //------------------------------------------------------------------------- /* Read user models belongs to object of 'group', that use readEach method Specify the get function pointer group model from user model. Group id list is listed automatically. */ query qe; /* Read group name only */ qe.select(_T("id"), _T("name")); gt.index(primary_key); gt.readEach(users, &user::grp, qe); /* Using the Origianl collection is same as. */ gt.readEach(m, &user::grp, qe); /* Group id list is listed by yourself. */ group_list_ptr grps(listup(users, &user::grp)); gt.index(primary_key).readEach(*grps, qe); /* Using the Origianl collection is same as. */ group_list_ptr grps2(listup(m, &user::grp)); gt.index(primary_key).readEach(*grps2, qe); /* Order by */ users.clear(); ut.index(0).keyValue(0).read(users, q.all()); sort(users, &user::name, &user::id); /* Order by for original collection */ m.clear(); ut.index(0).keyValue(0).read(m, q); sort(m, &user::name, &user::id); /* Using original sort function */ std::sort(begin(m), end(m), &sortFunc2); /* Using for_each function */ std::cout << "--- vector for each ---" << std::endl; std::for_each(users.begin(), users.end(), dumpUser); /* Using for_each function of original collection */ std::cout << "--- original collection for each ---" << std::endl; std::for_each(begin(m), end(m), dumpUser); /* Using only table operation */ table_ptr tb = ut.table(); tb->clearOwnerName(); } #pragma argsused int _tmain(int argc, _TCHAR* argv[]) { database_ptr db = createDatabaseObject(); try { connectParams param(_T("tdap"), _T("localhost"), _T("test"), _T("test")); /****************************************************** !!! Important !!! When using a multi-threaded, please request a new connection for each database. *******************************************************/ // When using a multi-threaded, set to true. bool newConnection = false; connectOpen(db, param, newConnection); databaseManager mgr(db); std::vector users; readUsers(mgr, users); std::cout << "Read records success.\nRecord count = " << users.size() << std::endl; return 0; } catch (bzs::rtl::exception& e) { std::tcout << _T("[ERROR] ") << *bzs::rtl::getMsg(e) << std::endl; } return 1; }