My friends, my life, my style - James S.F. Hsieh

2/19/2010

GANTZ 又回到了這房間...




軟體的藝術 trade off

今天看了篇文張 程式設計的兩個觀點:架構狂還是效率狂? 覺得十分有趣也心有所感, 以下是我的想法: 我也算是個架構狂, 比較執著於彈性與複用, 所以 dependency 與 role and responsibility 的設計就非常龜毛. 但最近面對的工作對效率比架構還有著較嚴苛的要求, 如此的要求下變的必須以效率為導向來決定整體的架構. 有著最佳的效率意味著整體都要互相配合, dependency 就變的比較強也比較無法複用, 但贏得的是效率的提升, 這也是我這一兩年來對於架構跟效率有著比較不同的體認, trade off 就是軟體的藝術, 對 效率 與 架構 也不例外 :)

2/18/2010

2010 02 14-18 Carcassonne


命運之輪是由小豬來轉動的....

2/12/2010

C++ Object Persistent for SQLite

上一篇文章我提到了 LiteSQL 的設計是利用 DB Schema (XML file) 來產生對應的 C++ Class, 而我的設計是從另一個角度出發 "設計 C++ 產生對應的 DB Schema" , 我設計了一系列的 Class, macro 跟  template function 來達到 Object Persistent 這個目的, 而 SQLite 接合的部份我選擇了 SQLite3++ 這個 SQLite3 backend library, 撰寫 Persistent Class 的時候只要利用這些東西就可以輕易的產生出可以 Persist 到 DB 的 class, 其中的功能包含簡易的 Reflection 跟 DB 對應的操作 function 諸如: CreateTable, InsertInto, UpdateAll, UpdateModified, SelectFromRow 等等... 以下是個使用的例子

Persistent class declaration

當我們利用 macro 寫出這個 Class 的時候, 這些 macro 讓我們描述了 class MLDBPersistSample 所對應的 DB Schema 跟 metadata , 因為繼承自 MLDBPersistBase class, 所以擁有了上述 DB 操作的能力, 使用的時候就不需要再去組合 SQL 字串了 :D 要建立 Table 只要呼叫 CreateTable 這個 static function


它就會根據我們所描述的 metadata 產生出以下的 SQL statement
CREATE TABLE MLDBPersistSample(Column1 TINYINT , Column2 SMALLINT , Column3 INTEGER PRIMARY KEY AUTOINCREMENT, Column4 BIGINT DEFAULT 0, Column5 TEXT , Column6 REAL NOT NULL, Column7 DATETIME , CHECK(Column1 >= 0 AND Column1 <= 5))
如果我們想要 insert 或是 update 一個 object 到 DB, 只要 (Column? 只是個例子, 實務上 Symbol 可以取得更有意義一點)


就能產生出以下的 SQL statement

INSERT INTO MLDBPersistSample (Column3) VALUES (?)
UPDATE MLDBPersistSample SET Column5 = ? WHERE Column3 = ?
 問號 ? 的部分, 會利用 data binding 的方式傳給 SQLite 的 API

Query 的部分, 我則是從 LiteSQL 例子中得到了一些啟發, 我實做了相對應的 query class 來產生對應的 SQL statement, 以下是個 C++ statement 與產生出 SQL statement 的結果

SELECT MLDBPersistSample.Column3,MLDBPersistSample.Column5 FROM MLDBPersistSample WHERE (MLDBPersistSample.Column3 >?) AND (MLDBPersistSample.Column5==?) ORDER BY MLDBPersistSample.Column1,MLDBPersistSample.Column2
我想這個部份是最有趣的, 也是 LiteSQL 最有創意的地方, 所以我也提供了這樣的使用方式, 利用這樣直覺得 C++ 操作可以產生出比較複雜一點點的 SQL query 真的是非常方便, 而且 C++ 的部分還會做 syntax checking, 而 10 跟 "hi" 的兩個 condition 會做 type checking, 並且兩個值會以 data binding 的方式傳給 SQLite API, 非常方便吧 :D

2/02/2010

LiteSQL - SQLite Object Persistent for C++

最近因為工作的原因需要用到 SQLite, Review 現有的 C++ Project, 我發覺我們現有 Project 在使用 SQLite 非常的不方便, 它使用最基本 (原始?!) 的方法自己組合 SQL statement 來操作 DB, 並建立一組 Class hierarchical 來 Modeling DB 的 Schema. 這樣設計的好處在於操作 DB 可以比較自主效能上也許會好些, Class hierarchical 也可以比較複雜. 壞處則是, 許多 DB 的操作 SQL statement 都有固定的 pattern 這個部份可以抽離出來, 但是 Class, statement 與 DB Schema 的對應關係需要人工來維護. 能夠盡量減少人力來維護是我的目標, 所以, 我們需要一個有彈性的 SQLite Object Persistent Library.

我 Survey 了一下現有的 C/C++ SQLite wrapper project, 有支援 Object Persistent 的似乎只有 LiteSQL 這個 open source project, 是用 LiteSQL 必須使用 XML 來定義 DB Schema, 並使用他們提供的 Code generator 來產生 C++ Modeling 的程式碼, 以下是官網上面的 example.

PersonDatabase db("sqlite3", "database=person.db");           
Person person(db);
person.name = "Bob"; // assign values to fields
person.age = 20;
person.update(); // writes a new record to database
我們可以輕易的操作 Person 物件來建立/改變 DB 中的 record, 該例子隱藏了 SQL statement 並且轉換成較我們較直覺得物件化操作, 對於 Relation 的部分, LiteSQL 可以建立 Relation-class 來 Modeling 兩個 Persistent Object 之間的關係, 這樣的設計在 C# 那種具有完整 Reflection 能力的語言中是非常容易實做, 但在 C/C++ 中則是非常困難.

除了Object Persistent 之外, LiteSQL 還提供了一下的 select 語句來方便查詢
PersonDatabase db("sqlite3", "database=person.db");
vector = select(db).all();
Person bob = select(db, Person::Name == "Bob").one();
這個 library 給我不少的啟發, 但我實際上試用它卻有不小的挫折, 原因在於我們使用的 DB Schema 似乎無法產生正確的 class, 畢竟這個計畫目前的狀態仍然是在 proof-of-concept implementation 的階段.

我遇到的另一個問題是, LiteSQL 是使用 Gen-Code 的方式來產生 Modeling Class, 所以, 如果 DB Schema 改變了, 程式碼應該有不小幅度的改變 (我想無法避免), 但是開發過程中 Schema 改變是免不了的, 所以我嘗試設計了一個以 class modeling 為主的方式來產生 DB Schema, 只要遵照一定的 class 設計方式, 就能夠利用類似 Reflection 的方式還產生 Schema 與 record 操作, 主要是配合 macro 跟 template 的方法來實現, 目前我還在設計中, 有機會再跟大家分享 :)