話說企業應用,通常離不開數據庫。要作數據庫,能夠有N種方案,好比:直接採用JDBC層本身封裝下使用的,採用一些框架的,如:iBatis,Hiberate,Spring JDBC Template等等(這個太多了,所以不一一列舉)的,這些方案也都在各自的領域展現了本身的特色,解決了至關部分的技術問題,並取得了至關好的應用效果。 java
可是不論是哪一種方案,其優勢和缺點每每也是連在一塊兒的,究其緣由是由於SQL和Java編程之間是割裂的,若是封裝得不到位,作Java的人太難使用;若是封裝得太多,在作一些用複雜SQL的時候又很是麻煩。好比:Hibernate就採用了封裝HQL的方式來解決這方面的問題。iBatis對於SQL支持比較好,可是又會有一些割裂感,同時在解決時還要引入動態SQL來解決須要根據一些運行時條件來處理的問題,必定程度上又增長了使用的複雜度。 git
那麼問題就來了,有沒有更好的方式來解決數據庫應用開發過程當中的問題呢?究其根本緣由是要如何解決數據庫開發中的SQL與Java代碼之間的割裂問題,若是能把這個問題解決掉,理論上會有一個不錯的解。 sql
咱們知道SQL實際是是一種數據爲領域的DSL語言,若是咱們能直接在Java中編寫SQL,而後執行結果就能夠直接返回Java對象,這個問題不就有了良好的解決方案麼? 數據庫
實際上這方面已經有一些現成的解決方案,可是有的不是開源的,有的支持的還不是很是到位,所以悠然就決定嘗試着寫一下,寫了半天時間看了看效果,詳見RESTful風格的支持實踐一文,內部討論了一下,感受還不錯,因而正式決定正式花時間來編寫一個TinySqlDsl,固然實際編寫的時候,仍是有許多的問題點的,以致於最終的風格與上面的文章還有一些不一致,固然這也是正常的,容易理解的,不然那什麼也太神了。 編程
咱們常見的SQL語句有Select、Insert、Update、Delete,所以咱們的方案中也實現了這幾個語句的編寫方式。 數組
首先來看看看TinySqlDsl版的Dao是怎麼寫的。 框架
public class Custom { private String id; private String name; private int age; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
public class CustomTable extends Table { public static final CustomTable CUSTOM = new CustomTable(); public final Column ID = new Column(this, "id"); public final Column NAME = new Column(this, "name"); public final Column AGE = new Column(this, "age"); private CustomTable() { super("custom"); } }
public class CustomDao { private DslSession dslSession; public DslSession getDslSession() { return dslSession; } public void setDslSession(DslSession dslSession) { this.dslSession = dslSession; } public void insertCustom(Custom custom) { dslSession.execute( insertInto(CUSTOM).values( CUSTOM.ID.value(custom.getId()), CUSTOM.NAME.value(custom.getName()), CUSTOM.AGE.value(custom.getAge()) ) ); } public void updateCustom(Custom custom) { dslSession.execute( update(CUSTOM).set( CUSTOM.NAME.value(custom.getName()), CUSTOM.AGE.value(custom.getAge())).where( CUSTOM.ID.eq(custom.getId()) ) ); } public void deleteCustom(String id) { dslSession.execute( delete(CUSTOM).where( CUSTOM.ID.eq(id) ) ); } public Custom getCustomById(String id) { return dslSession.fetchOneResult( selectFrom(CUSTOM).where( CUSTOM.ID.eq(id) ) , Custom.class); } public List<Custom> queryCustom(Custom custom) { return dslSession.fetchList( selectFrom(CUSTOM).where( and( CUSTOM.ID.eq(custom.getId()), CUSTOM.NAME.equal(custom.getName()), CUSTOM.AGE.equal(custom.getAge()) ) ) , Custom.class); } }看了上面的示例,會不會感受有點奇怪,怎麼能夠這麼寫?呵呵,先彆着急瞭解實際的實現機理,咱們先品味一下這種DSL風格的數據庫編寫方式,嗯嗯,具體的來講就是像寫SQL同樣的方式來寫SQL。
每一個數據表都要有兩個類進行映射,一個是POJO類,這個你們都很是熟悉就再也不花時間進行說明了,用於構建Dao代碼的時候使用。另外一個是表結構,用於在Java中定義數據庫的表結構。 函數
public class CustomTable extends Table { public static final CustomTable CUSTOM = new CustomTable(); public final Column ID = new Column(this, "id"); public final Column NAME = new Column(this, "name"); public final Column AGE = new Column(this, "age"); private CustomTable() { super("custom"); } }這個類主要由以下幾部分組成:
CustomTable對應於一個表結構類型,它繼承自Table類。 工具
構造函數,中的super("custom")使之與數據庫的表名進行映射。 fetch
public static final CustomTable CUSTOM = new CustomTable();這句定義了一個常量CUSTOM,對應於具備的表,它的用得中在DSL語法用要用到表的時候使用。
這個類裏定義了3個public成員變量,這些成員變量和具體的字段數相對應,表裏有幾個字段,這裏就定義幾個字段,這個實例化自Column。
OK,這樣表結構的定義就作好了。
正由於有了上面的定義,才能夠在Dao中用Java代碼像SQL同樣的編寫程序,可是這些語句是怎麼才能執行出結果的呢?這就要看DslSession的了。
DslSession是與數據庫打交道的類,說白了,它就是一個SQL執行器。
public interface DslSession { /** * 執行Insert語句關返回 * * @param insert * @return */ int execute(Insert insert); /** * 執行更新語句 * * @param update * @return */ int execute(Update update); /** * 執行刪除語句 * * @param delete * @return */ int execute(Delete delete); /** * 返回一個結果,既然是有多個結果也只返回第一個結果 * * @param select * @param requiredType * @param <T> * @return */ <T> T fetchOneResult(Select select, Class<T> requiredType); /** * 把全部的結果變成一個對象數組返回 * * @param select * @param requiredType * @param <T> * @return */ <T> T[] fetchArray(Select select, Class<T> requiredType); /** * 把全部的結果變成一個對象列表返回 * * @param select * @param requiredType * @param <T> * @return */ <T> List<T> fetchList(Select select, Class<T> requiredType); /** * 返回一個結果,既然是有多個結果也只返回第一個結果 * * @param complexSelect * @param requiredType * @param <T> * @return */ <T> T fetchOneResult(ComplexSelect complexSelect, Class<T> requiredType); /** * 把全部的結果變成一個對象數組返回 * * @param complexSelect * @param requiredType * @param <T> * @return */ <T> T[] fetchArray(ComplexSelect complexSelect, Class<T> requiredType); /** * 把全部的結果變成一個對象列表返回 * * @param complexSelect * @param requiredType * @param <T> * @return */ <T> List<T> fetchList(ComplexSelect complexSelect, Class<T> requiredType); }它的方法也比較簡單,主要功能就是執行這幾個語句。正是因爲把複雜的SQL都封裝到了Insert、Select、Update、Delete當中,所以這個執行器的接口方法反而是很是的簡單,正由於它太簡單了,所以根本就不須要介紹。僅僅要說明的是,當Select的時候,須要指定返回的類型,以便於告訴DslSession要返回的類型是什麼。
A:必須支持,不論是Union,子查詢,各類鏈接均可以支持
A:必須支持,不論是拼SQL語句分頁的仍是SQL默認就支持分頁的,均可以支持
A:不用,對於沒有給值的條件,框架會自動忽略此條件,因此你只要寫一個大而全的就能夠了。
A:必須支持,全部的函數均可以使用,只是若是寫了與某種數據庫相關的函數,跨數據庫時將再也不有兼容性。
A:必須支持,不論是多表聯合查詢仍是子查詢啥的,全都支持。
好像沒有啥不支持的,只有寫得漂亮不漂亮的,沒有支持不支持的。因爲支持自已編寫SQL片段,所以理論上你能夠用SQL片段完成全部的事情,只是看起來不夠漂亮而已。
若是使用Tiny元數據管理數據表,那麼只要在工具中以下操做,便可自動生成POJO、表定義、及Dao層代碼實現:
也就是隻要選中表定義文件,選擇右鍵->TinyStudio->生成DSL JAVA類,就能夠自動生成Dao層的全部代碼,若是須要能夠對生成的類進行修改或擴展,可是通常狀況下都足夠使用了。
若是沒有使用Tiny的元數據,那麼能夠本身寫個工具類來生成這幾個類,也能夠手工編寫,也能夠分分鐘編寫出來。
import static org.tinygroup.tinysqldsl.CustomTable.CUSTOM; import static org.tinygroup.tinysqldsl.Delete.delete; import static org.tinygroup.tinysqldsl.Insert.insertInto; import static org.tinygroup.tinysqldsl.Select.selectFrom; import static org.tinygroup.tinysqldsl.base.StatementSqlBuilder.and; import static org.tinygroup.tinysqldsl.Update.update;
任意一個方案都有它的優勢,也有它的缺點,TinySqlDsl也不例外,這裏簡單的分析一下,若是不全面,請同窗們下面補充,先謝謝了。
目前,咱們內部進行了試用,總體運行效果良好,後面準備主力推這種方式。
關心代碼的同窗,能夠查看下面的URL:http://git.oschina.net/tinyframework/tiny/tree/master/db/org.tinygroup.tinysqldsl
親,你有什麼意見、建議,請告訴咱們吧!