生成Java JDBC訪問代碼,從SQL文件
JDBC訪問數據庫
通過JDBC訪問數據庫,相信不少人都用過。比較辛苦,有很多的boilerplate。很多聰明的程序員也發現了這個問題,通過各種方式來解決這 個問題(當然,他們也為了解決另外的問題, boilerplate是其中之一),比如Hibernate,iBATIS,JdbcTemplate(Spring),等等。這幾種,各有各的優勢, 也有很多用戶。
最近在項目中,受Thrift,以及其它一些項目啟發,試著寫了一個程序,來自動生成JDBC的訪問代碼。
SQL,簡潔,表達力強
SQL,表達能力強,沒有多少boilerplate:
SELECT isbn, title, price, price * 0.06 AS sales_tax FROM Book WHERE price > 100.00 ORDER BY title limit 10;
程序:函數,參數,返回值
我們在寫程序時,可能價格是個參數,還需要支持分頁(limit, offset),返回的是List, 于是就是:
func list<Book> getBooksByPrice(float price, i32 limit, i32 offset) { SELECT isbn, title, price, price * 0.06 AS sales_tax FROM Book WHERE price > :price ORDER BY title limit :limit, :offset; }
這段話,表達是清楚的,并且沒有額外的“客套話”。 如果有一個程序,輸入是上面的code,生成的code是可以直接被調用的函數,將是理想情況。
java-jdbc
這里有個問題,Book未定義。當然,我們可以解析SQL語句,找到它來自Book表,繼而contact數據庫,找到Book表的metadata,能找到各個字段的數據類型, 就能定義Book了。如果這里面有Join的情況,會多聯系幾張表。 這樣引入了聯系數據庫的依賴,增加依賴一般是需要再三考慮的。另外,解析SQL,找出字段的來源去脈,推斷類型,不是一件簡單的工作。
但有個折中的辦法,引入一些冗余(為什么是冗余呢?因為這個Book定義的信息是可以推導出來的)
struct Book { string isbn string title float price float salesTax }
雖加入了一些冗余,貌似還可以接受。
我試著用Python寫了這個程序java-jdbc,輸入是上面的代碼,輸出是Java代碼(函數)。
示例
運行命令
python java_jdbc.py --input example.sf --out gen-java
就會在 gen-java目錄生成訪問數據庫的代碼。其中輸入文件(example.sf):
namespace java me.shenfeng struct Item { i32 id string name } // select func Item getItemById(i32 id) { select * form item where id = :id } func list<Item> getItems(int limit, int offset) { select * from item limit :limit, :offset } func list<Item> getItems(list<i32> ids) { select * from item where id in (:ids) order by FIELD(id, :ids) } // insert, return generated primary key func i32 saveItem(string name) { insert into item (name) value (:name) } // update func void updateItem(i32 id, string newName) { update item set name = :newName where id = :id } // delete func void deleteItemById(i32 id) { delete from item where id = :id }
生成的API:
相比其它方案的優勢,缺點,以及以后的方向
相比Hibernate,iBATIS,JdbcTemplate(Spring),java-jdbc優點:
- 理解成本低,幾乎是self-explained。
- runtime零依賴,生成的code不依賴任何第三方庫
- 對join等支持良好,對各個數據庫的“特殊” 特性,“原生”支持。
- 維護方便。維護SQL,幾乎是最簡單的,SQL的文檔也很多。
- 實現簡單 (300多行Python code)
缺點:
- 有的同學不是很喜歡SQL
- 功能單一,有些挺實用的功能,比如cache機制,讀寫分離機制,并沒有支持
后面的方向嘛,由于這相當于定義了新的語言,可以通過擴展語言的方式,來擴展功能,比如
// 最多cache 10000,通過lru策略淘汰,每個cache時間,最多3600s。 TODO @lru(size=10000, expire=3600s) func list<Book> getBooksByPrice(float price, i32 limit, i32 offset) { SELECT isbn, title, price, price * 0.06 AS sales_tax FROM Book WHERE price > :price ORDER BY title limit :limit, :offset; }
</div>
原文鏈接: http://shenfeng.me/java-jdbc-generate-boilerplate.html