一個輕量級dao處理框架

light-dao是一個輕量級的orm處理框架。
它的核心思想是盡可能簡單的使用原生sql诲侮。
它基于spring構(gòu)建传睹,可以和mybaties等其他數(shù)據(jù)處理框架配合使用。

一 由來


在我們的系統(tǒng)中合搅,主要使用Mybaties作為ORM處理框架。但是Mybaties相對重量級歧蕉,哪怕只是簡單使用,仍需要配置大量的xml康铭。比如下面這樣的配置

countryList = sqlMapper.selectList("<script>" +
        "select * from country " +
        "   <where>" +
        "       <if test=\"id != null\">" +
        "           id < #{id}" +
        "       </if>" +
        "   </where>" +
        "</script>", country, Country.class);

在簡單sql的情況下還可以接受惯退。當(dāng)sql再復(fù)雜一些,比如:

 <resultMap id="AssociationResultMap" type="com.test.mybatis.vo.MybatisOrder" >  
   <id column="ORDERID" property="orderid" jdbcType="DECIMAL" />  
   <result column="ORDERTYPE" property="ordertype" jdbcType="VARCHAR" />  
   <result column="ORDERDATE" property="orderdate" jdbcType="DATE" />  
  
<association property="customer" column="CUSTOMERID"   
    resultMap="com.test.mybatis.mapper.MybatiscustomerMapper.BaseResultMap"/>   
<collection property="itemList" column="ORDERID" javaType="ArrayList"   
    ofType="com.test.mybatis.vo.MybatisOrderItem"   
    resultMap="com.test.mybatis.mapper.MybatisOrderItemMapper.BaseResultMap"/>  
 </resultMap>  
 <select id="getOrderAssociation" parameterType="String" resultMap="AssociationResultMap">    
    SELECT *    
      FROM mybatisOrder ord LEFT JOIN mybatiscustomer customer ON ord.customerId = customer.ID   
      LEFT JOIN mybatisOrderItem item ON ord.orderid = item.orderid   
     WHERE ord.orderid = #{id}  
  </select>   

作為一個習(xí)慣了使用原生Sql的開發(fā)者來說从藤,實在是難以接受這種xml的設(shè)定催跪,尤其時當(dāng)一些純粹的數(shù)據(jù)查詢語句使用極為復(fù)雜的sql時,再對sql進(jìn)行一次xml配置實在是有心無力夷野。比如下面的這個sql:

select (IFNULL(payment, 0) - IFNULL(repayment, 0)) balance from
 (select sum(amount) payment from payment_instruction_0000 where mifi_id in
  (select distinct(t1.mifi_id) mifi_id from overdue_record t1, loan_contract_0000 t2
      where t1.status = 1
        and (t1.con_id = t2.id and t2.product_id in (76)))
    and product_id in (76)) as pay,
 (select sum(amount) repayment from repay_detail_instruction_0000 where repay_type in (1, 3, 7, 10) and mifi_id in
   (select distinct(t1.mifi_id) mifi_id from overdue_record t1, loan_contract_0000 t2
       where t1.status = 1
        and (t1.con_id = t2.id and t2.product_id in (76)))
    and product_id in (76)) as repay;

如果想改成xml配置......
真的做不到啊.......

在一次次的被mybaties的復(fù)雜配置搞的頭疼無奈之后懊蒸;
在一次次的xml配置失敗,無限重新上線之后悯搔;
在一次次吐槽無力后
——

終于下定決心自己寫一套dao處理框架骑丸,這套框架中堅決不要再出現(xiàn)任何關(guān)于sql的xml。
而且要滿足下面幾個要求:
1 支持原始的sql使用
2 支持sql的變量替換
3 支持sql返回結(jié)果的映射

二 示例


開發(fā)了一月有余妒貌,目前差不多完成通危,也已經(jīng)替換了線上的一些Mybaties部分。貼一下現(xiàn)在的示例代碼

@Dao(dbName = "my_db")
public interface InfoDao {
    String TABLE_NAME = " info ";
    String ALL_COLUMN = " id, information, user_id ";
    String ALL_VALUES = " {param.id}, {param.information}, {param.userId} ";

    @Data
    class Info { //查詢結(jié)果的Bean灌曙,也也可以是Thrift生成的Bean
        int id;
        String information;
        int userId;
    }

    @Execute("create table " + TABLE_NAME + " (id int, information varchar, user_id int)")
    void create();

    @Update("insert into " + TABLE_NAME + "(" +  ALL_COLUMN + ")" + " values(" + ALL_VALUES + ")")
    int insert(@SqlParam("param") Info info);

    @Data
    class UserInfo {
        String information;
        String name;
    }

    //跨表查詢示例
    @Select("select information, name from info, user" 
        + " where info.user_id = user.id")
    List<UserInfo> selectUserInfo();

而針對上面代碼的數(shù)據(jù)庫配置只有如下的簡單幾行:

<bean id="myDbDataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close" lazy-init="false">
    <property name="driverClassName" value="org.h2.Driver"></property>
    <property name="url" value="jdbc:h2:mem:mifi_internal_account"></property>
    <property name="username" value="sa"></property>
    <property name="password" value=""></property>
</bean>

任你的sql再復(fù)雜菊碟,所有的配置也只是針對數(shù)據(jù)庫的訪問。
再貼一個復(fù)雜sql的使用情況:

@Select("select count(1) from (select t1.mifi_id, IFNULL(sum(amount), 0) payment from payment_instruction_0000 t1, "
            + " loan_contract_0000 t2 where (account_time >= {startTime} and account_time < {endTime})"
            + " and (t1.con_id = t2.id and t2.product_id in {productList})"
            + " group by mifi_id) as pay left join (select t1.mifi_id, sum(amount) repayment from repay_detail_instruction_0000 t1, "
            + " loan_contract_0000 t2 where (account_time >= {startTime} and account_time < {endTime})"
            + " and (t1.con_id = t2.id and t2.product_id in ({productList}))"
            + " and (t1.repay_type in (1, 3, 7, 10))"
            + " group by mifi_id) as repay on pay.mifi_id = repay.mifi_id where payment - IFNULL(repayment, 0) > 0")
long getBalanceNum(@SqlParam("startTime") long startTime, @SqlParam("endTime") long endTime, @StringParam("productList") String productList)

基本就像把原生sql直接傳進(jìn)去一樣在刺。

三 源碼講解:


源碼基本上分為三層
1 spring層逆害,用于向spring的BeanFactory注冊用戶自定義的Dao接口
2 executor層头镊,將Dao接口中的每一個方法組裝成一個一個的executor執(zhí)行體
3 數(shù)據(jù)庫層,執(zhí)行實際的sql
結(jié)構(gòu)圖如下:


層級結(jié)構(gòu)圖

具體實現(xiàn)部分請參考源碼魄幕,注釋比較豐富相艇,實現(xiàn)也相對比較直觀。

四 使用方式


1 聲明一個Dao接口類

@Dao(dbName = "my_db") //必須使用@Dao注解 必須指明數(shù)據(jù)庫的名字(dbName)
public interface InfoDao     //接口名必須以Dao結(jié)尾(方便被Resolver發(fā)現(xiàn))

2 將sql語句定義為方法

@Select("select id, name from " + TABLE_NAME + " where id = {id}")
User select(@SqlParam("id") int id); 

a sql語句的注解必須為@Select, @Update, @Execute三種之一梅垄,一般來說厂捞,select表示查詢語句,update表示有寫入的語句队丝,比如insert和update靡馁,execute表示ddl語句,比如create table這種

b 在sql語句中机久,請將參數(shù)使用{}來標(biāo)記臭墨。一般來說,可以標(biāo)記正常的命名參數(shù)膘盖,比如{id}胧弛,還可以標(biāo)記bean的屬性參數(shù),比如{user.name}

c 函數(shù)參數(shù)也有兩種標(biāo)記方式侠畔,@StringParam和@SqlParam结缚,被@StringParam標(biāo)記的參數(shù)將直接替換sql中同名的參數(shù),比如"select {name} from user" @StringParma String name("littlersmall"),sql語句將被替換為"select littlersmall from user"软棺。通常用于一些可變字符串的直接替換红竭。
被@SqlParam標(biāo)記的參數(shù)有兩種形式,普通的命名參數(shù)和bean的屬性參數(shù)喘落,而且參數(shù)可以是任意類型茵宪,比如:
{id} @SqlParam int id;
{user.name} @SqlParam User user(無需再寫user.name);
該方式主要用于sql的參數(shù)替換

d 返回值通常有幾種:
(1) select類型,可以返回基本類型瘦棋,比如int, long等稀火,還可以返回用戶自定義的Bean類型,比如User比如Thrift生成的bean赌朋,還可以返回多條結(jié)果凰狞,使用一個List,比如List<User>, List<String>
(2) Update類型沛慢,只能返回int服球,表示插入或修改的行數(shù)
(3) Execute類型無返回值

3 使用Dao接口

@Service //一定是被spring管理的類
public class UserDaoExample {    
    @Autowired    UserDao userDao;  //直接Autowired就好
    ...
}

4 數(shù)據(jù)庫配置,請寫在applicationContext.xml中颠焦,例如

<bean id="MyDbDataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close" lazy-init="false">
    <property name="driverClassName" value="org.h2.Driver"></property>
    <property name="url" value="jdbc:h2:mem:mifi_internal_account"></property>
    <property name="username" value="sa"></property>
    <property name="password" value=""></property>
</bean>

a 請使用數(shù)據(jù)庫名作為beanId的前綴斩熊,比如本例中,數(shù)據(jù)庫的名字為my_db伐庭,使用大寫的駝峰命名方式MyDb
b beanId結(jié)尾部分請使用DataSource

五 源碼地址


github地址

https://github.com/littlersmall/light-dao

路過的幫忙點個星星啊粉渠,謝謝_ 分冈。
請直接使用mvn package獲得項目的jar包
或者通過mvn deploy的方式將其構(gòu)建到自己的中央庫中

六 參考


項目參考了大量的網(wǎng)上資料,感謝baidu霸株,google雕沉,bing,soso
參考了部分mybaties實現(xiàn)去件,部分spring實現(xiàn)坡椒,以及paoding-rose框架

七 總結(jié)


1 有一個大致的方向之后就應(yīng)該馬上動手,越拖延越?jīng)]有動力
2 java的反射確實很強(qiáng)大
3 每個類尤溜,每個函數(shù)盡量只做一件事情
4 反復(fù)的重構(gòu)很有必要倔叼,而且也很有收獲
5 再復(fù)雜的代碼,也是由最簡單的東西一步步進(jìn)化而來

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宫莱,一起剝皮案震驚了整個濱河市丈攒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌授霸,老刑警劉巖巡验,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異碘耳,居然都是意外死亡显设,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進(jìn)店門辛辨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捕捂,“玉大人,你說我怎么就攤上這事愉阎。” “怎么了力奋?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵榜旦,是天一觀的道長。 經(jīng)常有香客問我景殷,道長溅呢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任猿挚,我火速辦了婚禮咐旧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绩蜻。我一直安慰自己铣墨,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布办绝。 她就那樣靜靜地躺著伊约,像睡著了一般姚淆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上屡律,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天腌逢,我揣著相機(jī)與錄音,去河邊找鬼超埋。 笑死搏讶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的霍殴。 我是一名探鬼主播媒惕,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼繁成!你這毒婦竟也來了吓笙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤巾腕,失蹤者是張志新(化名)和其女友劉穎面睛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尊搬,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡叁鉴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了佛寿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幌墓。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖冀泻,靈堂內(nèi)的尸體忽然破棺而出常侣,到底是詐尸還是另有隱情,我是刑警寧澤弹渔,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布胳施,位于F島的核電站,受9級特大地震影響肢专,放射性物質(zhì)發(fā)生泄漏舞肆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一博杖、第九天 我趴在偏房一處隱蔽的房頂上張望椿胯。 院中可真熱鬧,春花似錦剃根、人聲如沸哩盲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽种冬。三九已至镣丑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間娱两,已是汗流浹背莺匠。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留十兢,地道東北人趣竣。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像旱物,于是被迫代替她去往敵國和親遥缕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容

  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,811評論 0 11
  • 1. 簡介 1.1 什么是 MyBatis 宵呛? MyBatis 是支持定制化 SQL单匣、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,523評論 0 4
  • ORACLE自學(xué)教程 --create tabletestone ( id number, --序號usernam...
    落葉寂聊閱讀 1,083評論 0 0
  • java事務(wù)的處理 轉(zhuǎn) https://www.cnblogs.com/Bonker/p/5417967.html...
    小小的Jobs閱讀 1,397評論 0 1
  • 在之前的內(nèi)容中,我寫了Java的基礎(chǔ)知識宝穗、Java Web的相關(guān)知識户秤。有這些內(nèi)容就可以編寫各種各樣豐富的程序。但是...
    一葉障目閱讀 249評論 0 0