像寫SQL同樣編寫Java數據應用-TinySqlDsl

前言

話說企業應用,通常離不開數據庫。要作數據庫,能夠有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對象,這個問題不就有了良好的解決方案麼? 數據庫

TinySqlDsl解決方案

實際上這方面已經有一些現成的解決方案,可是有的不是開源的,有的支持的還不是很是到位,所以悠然就決定嘗試着寫一下,寫了半天時間看了看效果,詳見RESTful風格的支持實踐一文,內部討論了一下,感受還不錯,因而正式決定正式花時間來編寫一個TinySqlDsl,固然實際編寫的時候,仍是有許多的問題點的,以致於最終的風格與上面的文章還有一些不一致,固然這也是正常的,容易理解的,不然那什麼也太神了。 編程

咱們常見的SQL語句有Select、Insert、Update、Delete,所以咱們的方案中也實現了這幾個語句的編寫方式。 數組

首先來看看看TinySqlDsl版的Dao是怎麼寫的。 框架

第一步:定義POJO

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");
    }
}

第三步:編寫DAO類

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的了。

DslSesssion

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要返回的類型是什麼。

Q&A

Q:是否是支持複雜的SQL?

A:必須支持,不論是Union,子查詢,各類鏈接均可以支持

Q:是否是支持分頁?

A:必須支持,不論是拼SQL語句分頁的仍是SQL默認就支持分頁的,均可以支持

Q:你這個SQL條件一路寫下來,是否是須要全部的條件都必須存在?

A:不用,對於沒有給值的條件,框架會自動忽略此條件,因此你只要寫一個大而全的就能夠了。

Q:是否是支持數據庫中的函數?

A:必須支持,全部的函數均可以使用,只是若是寫了與某種數據庫相關的函數,跨數據庫時將再也不有兼容性。

Q:是否是支持多表關聯查詢?

A:必須支持,不論是多表聯合查詢仍是子查詢啥的,全都支持。

Q:有啥不支持的不?

好像沒有啥不支持的,只有寫得漂亮不漂亮的,沒有支持不支持的。因爲支持自已編寫SQL片段,所以理論上你能夠用SQL片段完成全部的事情,只是看起來不夠漂亮而已。

應用實踐

支持類編寫

使用Tiny元數據開發

若是使用Tiny元數據管理數據表,那麼只要在工具中以下操做,便可自動生成POJO、表定義、及Dao層代碼實現:

也就是隻要選中表定義文件,選擇右鍵->TinyStudio->生成DSL JAVA類,就能夠自動生成Dao層的全部代碼,若是須要能夠對生成的類進行修改或擴展,可是通常狀況下都足夠使用了。

自行編寫或生成

若是沒有使用Tiny的元數據,那麼能夠本身寫個工具類來生成這幾個類,也能夠手工編寫,也能夠分分鐘編寫出來。

DAO編寫注意事項

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;



這裏用到一個技巧,就是經過靜態引入這些要用到的語句或表定義,這樣才能夠方便的編寫DSL格式的語句。

優缺點對比

任意一個方案都有它的優勢,也有它的缺點,TinySqlDsl也不例外,這裏簡單的分析一下,若是不全面,請同窗們下面補充,先謝謝了。

優勢

  1. 熟悉SQL的同窗,上手很是方便,能夠說熟悉SQL的同窗,能夠很是快的上手,甚至不會Java均可以快速編寫
  2. 即時提示效果很是好,全部的IDE都提供的語法提示,使得編寫SQL時,對於表結構沒必要再記得一清二楚,第一編寫速度快許多,第二不用擔憂拼寫錯誤而花費大量的調試時間
  3. SQL的構建和Java的處理一體化完成,開發過程沒必要兩個部分先分開再分離
  4. 完美的解決動態SQL方面的問題,不須要複雜的配置,不須要複雜的處理,一切渾然天成
  5. 像寫SQL同樣寫Java數據庫業務代碼

缺點

  1. 這種方式畢竟和寫SQL仍是有一點區別,須要花一點時間熟悉
  2. 更多的親們在下面補充

總結

目前,咱們內部進行了試用,總體運行效果良好,後面準備主力推這種方式。

關心代碼的同窗,能夠查看下面的URL:http://git.oschina.net/tinyframework/tiny/tree/master/db/org.tinygroup.tinysqldsl

親,你有什麼意見、建議,請告訴咱們吧!

相關文章
相關標籤/搜索