DAS是信也科技自研的數據庫訪問中間件。是集數據庫管理,ORM,動態SQL構建和分庫分表支持的一體化關係型數據庫訪問解決方案。java
看到這裏,你必定會說少年啊!已經有了那麼多ORM框架和分庫分表組件,像Hibernate,Mybatis,mycat,sharding jdbc,還有咱們最愛的攜程DAL可供選擇,幹嗎還要重複造輪子?git
答案很簡單,這些工具都很差用!而DAS是咱們最新發明的高科技輪子。程序員
警告!前方高能!非資深開發人員儘快撤離。請握緊鼠標,抓牢鍵盤!github
你必定奇怪,既然要作數據庫中間件,爲何不像其餘產品那樣,從JDBC或者數據庫協議層入手,在傳統數據庫上面作分庫分表或從新開發數據庫引擎?那樣多牛逼啊!現有程序不用改就能夠無縫移植。爲何DAS還要提供ORM功能呢?sql
回答這個問題以前,讓咱們先簡單回顧一下流行的數據庫編程過程。這有助於理解DAS的產品定位。數據庫
mybatis,Hibernate發明的年代尚未什麼數據庫垂直與水平擴容,分庫分表之類的概念,天然也不會從設計上加以考慮。而如今隨便一個互聯網公司,天天產生的數據都是天量。所以一個正經的數據庫項目每每會同時用到ORM工具和分庫分表組件。不管是ORM仍是分庫分表組件,通常都須要繁瑣的配置。區別只在於難度級別是受得了仍是勸退。編程
以最流行的mybatis+任意分庫分表組件爲例,若是你是一個資深的CRUD boy,確定很是熟悉下面的套路,在開始寫下圖中間最終實際的DAO代碼以前,你須要先搞定另外四件事情:安全
當你手忙腳亂搞好這些配置,第一次測試時候,十有八九不會成功,這個時候千萬不要氣餒,由於更慘的還在後面,當在項目中使用獨立的ORM和分庫分表組件時,你會難過到流淚:session
而隨着庫,表的增加,作這些事情的痛苦指數從痛苦向無限痛苦飛速發展。若是你以爲這沒什麼,那你必定是能享受福報的那一批人mybatis
當最終克服萬難代碼能工做時,你會發現跟整體的配置和代碼量相比,最終的DAO代碼只佔不多一部分。而就這一部分代碼裏面,真正有用的也只是極小一部分,不信你看:
public static void main(string[] args) throws IOException { Inputstrean resourceAsstream Resources ogetResourceasstrean("cc/sq1MupConfig.xml"); sq1SessionFactory ssfenew sq1Session actoryBullder() .build(resourceAsstream); ///mapper就是UserMapper接口的實現類 UserMapper mapper = sqlSession. getMapper(UserMapper.class); User u = mapper.finduserById(10); system.out.print1n(); }
上面這段典型的mybatis代碼。除了倒數第二行代碼,其餘的都是什麼玩意?只是搶個兩分錢的紅包,幹嗎要磕這麼多響頭?刨去註釋和無關代碼,這裏面真正有用的代碼只佔1/5而已。你不以爲這個比例荒謬到好笑嗎?爲交付這一點點代碼,付出的代價如此之大,
是否是以爲哪不對?
其實要查詢,真正關鍵的信息就是數據庫名和查詢語句而已。評價一個設計的的好壞只要看實現一個需求在多大程度上只須要提供必要信息。額外步驟越多,設計越失敗!做爲參考,請思考餐廳用餐和本身買菜作飯的區別。
做爲一個老程序員,我已經厭倦了使用破爛工具。人嘛,要對本身好一點,誠實一點。一我的性化的數據庫訪問框架應該是這個樣子:
因而2018年在時任CTO的規劃下,咱們信也科技基礎組件團隊決定本身動手搞一套符合本身心意的數據庫訪問中間件,這就是
信也DAS
DAS是Database Access Service的縮寫。DAS的目標就是給研發人員提供一個一站式的數據庫訪問框架,讓研發人員用最簡單直接的方式開發數據庫訪問代碼,實現上面全部非分的想法
爲實現這個目標,DAS提供:
但DAS的真正的核心優點不是這些組件,咱們build了一個專業的團隊,7*24小時主動爲程序員服務,幫你們搞定從原子到宇宙尺度的任何數據庫問題
在信也科技,研發人員發郵件告訴DAS團隊各個環境的數據庫配置和邏輯數據庫信息,DAS團隊經過DAS Console配置好並自動同步到公司的配置中心後,用戶只要在本身的項目裏面引入DAS Client的依賴就能夠開始直接寫代碼。對,你沒有看錯,直接開始寫代碼,無需任何的本地配置工做。咱們把中間件產品的研發從交付組件提高到交付服務的層面
這,纔是咱們成功的祕密![撒花]
你心中必定冷笑,吹吧你!那讓咱們從技術角度看看 DAS的核心DAS Client 到底長什麼樣
DAS Client的設計聽從分層抽象原則,從上到下分爲:
DAO層是程序員使用最頻繁的部分,今天會重點介紹這一部分,其餘部分會在將來會逐一提供,請關注咱們的拍碼場公衆號。
DAS ORM的主要由預約義DAO類DasClient,SQL建立工具類SqlBuilder和特殊操做指令類Hints組成。下面一一介紹。
DAS ORM的核心是DasClient類,來看看裏面提供了啥方法:
DasClient提供了幾乎全部常見的ORM操做,開箱即用,不須要用戶生成任何DAO接口或實現
別跟我扯犢子,上代碼!
OK!猜猜看用DAS實現一個查詢操做須要幾行代碼?
Person pk = new Person(); pk.setName("test"); DasClient dao = DasClientFactory.getClient("logicDbName"); List<Person> plist = dao.queryBySample(pk);
客戶端建立到使用,兩行代碼完事,是否是很簡單粗暴?像我說的同樣,若是你要完成一個查詢,你須要提供就只是數據庫名和SQL,這裏SQL用sample data表示。除此之外,沒有多餘動做。沒有session,沒有事務,也沒有connection。只要寫的代碼足夠少,BUG就不會追上我。這就是傳說中的極簡編程風
經過這種預約義API的方式能節省多少代碼呢?再以一個實際例子對比一下完成一樣功能mybatis和DAS之間代碼量:
Mybatis mapping:
<select id="selectByExample" parameterType="com.ppdai.xxxxxxxxxxxxxxxxxxx.StrategyAccountDetailExample" resultMap="BaseResultMap"> select <if test="distinct"> distinct </if> 'false' as QUERYID, <include refid="Base_Column_List" /> from strategyaccountdetail${tableSuffix} <if test="_parameter != null"> <include refid="Example_Where_Clause" /> </if> <if test="orderByClause != null"> order by ${orderByClause} </if> </select>
DAS對應代碼:
public List<Strategyaccountdetail> selectByExample(Strategyaccountdetail detail) throws SQLException { return client.queryBySample(detail); }
看到區別了嗎?在不須要寫一行XML的狀況下,DAS用一行代碼就能夠搞定 mybatis須要十幾行,甚至幾十行配置才能完成的功能。其實上面顯示的還只是完成這個功能完整mybatis配置的一小部分配置,不過已經足夠說明我並無吹牛
你必定會想,按樣例查詢這個例子仍是很是容易提供通用實現的,若是要根據各類條件生成複雜,動態的SQL怎麼辦?是否是要寫不少if-else語句本身拼?圖樣!這時候就要SqlBuilder出馬了。仍是讓咱們看看實際的代碼對比:
Mybatis mapping:
<select id="selectListByUserIdExample" parameterType="java.util.Map" resultMap="BaseResultMap"> select * from (select ROW_NUMBER() OVER ( ORDER BY inserttime DESC ) rownum, <include refid="Base_Column_List" /> from strategyaccountdetail${tableSuffix} WITH(NOLOCK) where userid = #{userid,jdbcType=INTEGER} <if test="strategyid != null and strategyid != ''"> and strategyid = #{strategyid,jdbcType=VARCHAR} </if> <if test="typeid != null"> and typeid = #{typeid,jdbcType=INTEGER} </if> <if test="beginInserttime != null"> and inserttime <![CDATA[>= ]]> #{beginInserttime,jdbcType=TIMESTAMP} </if> <if test="endInserttime != null"> and inserttime <![CDATA[<= ]]> #{endInserttime,jdbcType=TIMESTAMP} </if> AND isactive=1) tpage WHERE tpage.rownum BETWEEN ${startPage} AND ${pageSize} </select>
DAS對應代碼:
public List<Strategyaccountdetail> selectListByUserIdExample(Long userId, String strategyid, Integer typeId, Date beginInserttime, Date endInserttime, Integer pageNum, Integer pageSize) throws SQLException { SqlBuilder builder = SqlBuilder.selectAllFrom(definition).where().allOf(definition.Userid.eq(userId),definition.Isactive.eq(1), definition.Strategyid.eq(strategyid).nullable(), definition.Typeid.eq(typeId).nullable(),definition.Inserttime.greaterThanOrEqual(beginInserttime).nullable(), definition.Inserttime.lessThanOrEqual(endInserttime).nullable()). orderBy(definition.Inserttime.desc()).into(Strategyaccountdetail.class).offset(pageNum, pageSize).withLock(); return client.query(builder); }
使用SqlBuilder的DAS的code是否是仍是同樣緊緻光滑?有人會說最新的mybatis也有SqlBuiler嘛。那咱們就也比一比,不要說我騙人:
Mybatis Sql builder:
public string selectPersonLike(final String id, final String firstName, final string lastlame) 《 return new SQL() { { SELECT("P. ID, P.USERNAIE, P.PASSHORD, P.FIRST _NANE, P.LAST NAME"); FROM("PERSON P") if (id != null) { WHERE("P.ID like#{id}"); } if (firstlame != null) { WHERE("P.FIRST MAE like #{firstliase}"); } if (lastlame != null) { WHERE("P.LAST NAMIE like #{lastName}"); } ORDER BY("P.LAST. NAME"); } }.toString(); }
DAS SqlBuilder:
public SqlBuilder seletPersonLike(final string id, final String firstlane, final string lastName) { Person.PersonDefinition P = Person.PERSON; return sqlBuilder.selectAllFrom(p) where(). allOf( p.d.like(id).nullable(), p.firstName.like(firstNane).nullable(), p. lastNare .1ike(iastName).nullab1e() ).orderBy(p.lastName); }
明顯仍是DAS的SqlBuilder設計更出色!
一步到位的提供API會存在一個設計風險,那就是任何操做都會存在特殊狀況。好比一個簡單的插入操做,就存在不少變體:
普通的作法是爲每種特殊作法提供overload的方法,有幾種特殊狀況就提供幾個方法。按照這種思路發展下去,方法的數量很快就會多到失控。如何才能確保在一個精簡的API集合上提供儘量多的特殊操做呢?這就輪到Hints登場了。
你可能注意到DasClient的方法除了必要參數外,每每還會帶一個Hints。這個Hints要麼是以可變參數存在,要麼是做爲必要參數的一個屬性。DAS利用Hints傳遞特殊指令,幫助用戶處理靈活多變的場景。以插入單條記錄爲例,API長這樣:
public <T> int insert(T entity, Hints...hints) throws SQLException
調用的時候既能夠只傳entity:
dao.insert(p);
也能夠傳最多一個hints
dao.insert(p, hints.insertWithId());
不管哪一種狀況,方法只有一個。
雖然Hints也算不上腦洞特別大的發明,但與ORM結合得如此之緊密天然,別無分號。這種設計帶來的便利是巨大的。不信能夠參考一下若是用獨立的分庫分表組件會怎樣實現:
// Sharding database and table with using hintManager , String sql = "SELECT * FROM t order"; try (HintManager hintManager = HintManager.getInstance(; Connectlon conn = dataSource.getConnection(); PreparedStatement preparedstatement conn. prepareStatement(sq1)) { hintManager.addDatabaseShardingValue("t_order", 1); hintManager.addTableShardingValue("t_order", 2); try (ResultSet rs = preparedStatement.executeQuery()) { while (rs.next()) { //... } } }
上面須要3行獨立代碼完成的Hints相關工做。倒不是說這個分庫分表組件設計的很差。除了TiDB或Amazon Aurora這種真正的分佈式數據庫以外,絕大多數基於傳統數據庫之上的分庫分表組件都難以作到徹底的對應用代碼透明。在特殊場景下都須要以某種方式傳遞特殊指令。若是依賴於現有ORM工具或基於JDBC,就會存在相似上面這種很不天然的代碼。
而DAS經過將Hints與ORM接口結合的方式,完美的解決了特殊與通常的矛盾。一樣的事情DAS只須要一行:
List<Person) plist = dao.query(selectAllFrom(p). setHints(Hints.hints().shardValue(1).tableShardValue(2)));
在推廣過程當中咱們還發現一個有趣的事情。就是咱們覺得用戶喜歡透明的分庫分表,但事實上,出於各類緣由,用戶用的最多的反而是直接指定分庫分表。固然利用Hints能夠很簡單的作到:
List<Person> plist = dao.query(selectAllFrom(p).setHints(Hints.hints(). inShard(1).inTableShard(2)));
自研ORM還有一個額外的好處。那就是雖然從成本還有技術的方面來看,分庫分表技術目前還有市場,但長遠來看,這大機率是一種過渡性的技術。即便哪天人們徹底解決了分佈式數據庫的性能和一致性問題,也仍是須要某種面向應用的ORM技術來實現靈活多變的需求。這樣DAS就能夠繼續發揮做用。從今天的標準來看,DAS ORM的設計在易用性和靈活性上已經達到了能達到的極限。
DAS完美結合了ORM和分庫分表功能,其產品定位是進可攻,退可守。根據公司內部實際使用效果來看,使用DAS能極大提升研發效率,減小代碼量和出錯機率,再也沒有因配置致使的各類故障。
有一次偶爾路過聽到一個總監和下面tech leader的對話,總監問若是技術輸出,新的代碼裏面可否不用咱們的DAS,leader微笑着但堅決的回答,不行,DAS很好用的,我要用。
對咱們作框架的程序員來講,還有什麼比一句好用更高的評價嗎?
是好東西就要拿出來你們一塊兒用,DAS已經開源,並提供了詳盡的文檔供你們參考,請你們盡情star
GitHub地址:https://github.com/ppdaicorp/das
除了開源文檔,咱們還提供在線技術支持,有興趣的朋友能夠入羣得到幫助或者更多活動信息
最後說一句,不要重複造輪子是最廣爲人知的謬誤。你不造,只是把機會讓給別人。
做者介紹
Hejiehui,信也科技基礎組件部門主管、信也DAS產品負責人、佈道師。圖形化構建工具集x-series的做者。曾主持開發攜程開源數據庫訪問框架DAL。對應用開發效率提高和分佈式數據庫訪問機制有多年的研究積累