Mybatis生產(chǎn)力工具 mybatis generator

mybatis是數(shù)據(jù)庫開發(fā)最常用的ORM框架,目前有很多碼農(nóng)仍然采用傳統(tǒng)的手工編寫sql語句的方式來開發(fā),
經(jīng)常出現(xiàn)的問題是工作量大,字段名拼寫錯(cuò)誤,排錯(cuò)困難,特別是做老代碼維護(hù),更是巨大的陷阱,雷同語句多,誰都不敢動(dòng),需求來了只能新增語句(這也符合開閉原則,哈哈),面對(duì)系統(tǒng)不斷增加的熵值,你該如何解呢?

我給大家介紹一款生產(chǎn)力工具mybatis generator.它是一款mybatis 代碼生成器,可以自動(dòng)生成dto,dao及sqlmapper.xml,其中的亮點(diǎn)是自動(dòng)生成可編程的動(dòng)態(tài)查詢構(gòu)造器--Example,采用這款工具基本可以應(yīng)付90%的開發(fā)場景,都可以不用寫sql語句.更值得一提的是

使用非常簡單!!!

下面簡要介紹下這款工具
本文采用Maven插件mybatis-generator-maven-plugin來配置MyBatis Generator

準(zhǔn)備條件:maven工程,mysql庫

1. 增加maven依賴

<dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.43</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.0</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>

2. 增加maven插件

<build>
    <plugins>
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.2</version>
            <configuration>
                <verbose>true</verbose>
                <overwrite>true</overwrite>
                <!--mybatis generator配置文件-->
                <configurationFile>src/main/resources/db-init/generatorConfig.xml</configurationFile>
            </configuration>
    
            <dependencies>
                <!-- 數(shù)據(jù)庫驅(qū)動(dòng)  -->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>${mysql.version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

3. 編輯mybatis-generator配置文件

<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
    PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
    "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

    <generatorConfiguration>
        <context id="mysqlTables" targetRuntime="MyBatis3">
            <commentGenerator>
                <property name="suppressDate" value="false"/>
                <property name="suppressAllComments" value="true"/>
            </commentGenerator>
            <!--目標(biāo)數(shù)據(jù)庫配置-->
            <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                    connectionURL="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;nullNamePatternMatchesAll=true"
         userId="root" password="root"/>
            <!-- 指定生成的類型為java類型滨嘱,避免數(shù)據(jù)庫中number等類型字段 -->
            <javaTypeResolver>
                  <property name="forceBigDecimals" value="false"/>
            </javaTypeResolver>
            <!-- 生成model模型,對(duì)應(yīng)的包,存放位置可以指定具體的路徑,如/ProjectName/src鸵鸥,也可以使用MAVEN來自動(dòng)生成 -->
            <javaModelGenerator targetPackage="org.test.repo.dto" targetProject="src/main/java">
                <property name="enableSubPackages" value="false"/>
                <property name="trimStrings" value="true"/>
                <property name="immutable" value="false"/>
            </javaModelGenerator>
            <!--對(duì)應(yīng)的xml mapper文件  -->
            <sqlMapGenerator targetPackage="org.test.repo.dao.mapper"  targetProject="src/main/java">
                <property name="enableSubPackages" value="false"/>
            </sqlMapGenerator>
            <!-- 對(duì)應(yīng)的dao接口 -->
            <javaClientGenerator type="XMLMAPPER" targetPackage="org.test.repo.dao" targetProject="src/main/java">
                <property name="enableSubPackages" value="false"/>
            </javaClientGenerator>
              <!--定義需要操作的表及對(duì)應(yīng)的DTO名稱-->
            <table tableName="order" domainObjectName="OrderDTO"/>
            <table tableName="order_item" domainObjectName="OrderItemDTO"/>
        </context>
</generatorConfiguration>

4. 生成dto,dao和mapper.xml文件

image.png

雙擊運(yùn)行mybatis-generator.generate插件會(huì)自動(dòng)生成dto,dao,example和mapper.xml文件

也可以通過運(yùn)行mvn mybatis-generator:generate命令來生成代碼

生成文件如圖所示

image.png

我創(chuàng)建了order,order_item兩張表做例子,紅色圈自動(dòng)生成的動(dòng)態(tài)查詢構(gòu)造器Example,每張表都生成一個(gè)對(duì)應(yīng)的Example類.

5. mapper類介紹

mapper也就是數(shù)據(jù)庫訪問類dao,我們看下都有什么內(nèi)容

package org.dev.repo.dao;

import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.dev.repo.dto.OrderDTO;
import org.dev.repo.dto.OrderDTOExample;

public interface OrderDTOMapper {
    //通過條件計(jì)數(shù)
    int countByExample(OrderDTOExample example);
    //通過條件刪除
    int deleteByExample(OrderDTOExample example);
    //通過主鍵刪除
    int deleteByPrimaryKey(Long id);
    //插入DTO
    int insert(OrderDTO record);
    //dto非空字段插入
    int insertSelective(OrderDTO record);
    //條件查詢,返回結(jié)果包括text等大字段
    List<OrderDTO> selectByExampleWithBLOBs(OrderDTOExample example);
    //條件查詢,返回結(jié)果不包括text等大字段
    List<OrderDTO> selectByExample(OrderDTOExample example);
    //通過主鍵查詢
    OrderDTO selectByPrimaryKey(Long id);
    //條件更新,只更新dto中非空字段的值
    int updateByExampleSelective(@Param("record") OrderDTO record, @Param("example") OrderDTOExample example);
    //條件更新全部字段,并包含text等大字段
    int updateByExampleWithBLOBs(@Param("record") OrderDTO record, @Param("example") OrderDTOExample example);
    //條件更新dto全部字段
    int updateByExample(@Param("record") OrderDTO record, @Param("example") OrderDTOExample example);
    //通過主鍵更新,dto非空字段
    int updateByPrimaryKeySelective(OrderDTO record);
    //通過主鍵更新,dto全部字段,包含text等大字段
    int updateByPrimaryKeyWithBLOBs(OrderDTO record);
    //通過主鍵更新,dto全部字段,不包含text等大字段
    int updateByPrimaryKey(OrderDTO record);
}

通過OrderDTOMapper.java,我們看出基本我們常用的創(chuàng)建,修改,查詢和刪除都有了.

我們來看下幾個(gè)常用方法:

  • 最常用的動(dòng)態(tài)查詢方法selectByExample(OrderDTOExample example),簡單通過構(gòu)造不同的example對(duì)象就可以實(shí)現(xiàn).
  • 后綴為Selective的插入和更新方法,只處理dto對(duì)象的非空字段,例如經(jīng)常我們只想更新某些記錄的部分字段,那么updateByExampleSelective方法最合適不過.
  • 后綴為WithBLOBs的方法是對(duì)大字段的處理方法,與常規(guī)處理方法分離,也體現(xiàn)了作者的周到.

6. 動(dòng)態(tài)查詢例子

我們看下如何使用Example來進(jìn)行動(dòng)態(tài)查詢

例如:我們要查詢id為123456用戶在2017年1月1日后金額在300元以上的所有訂單,并按照創(chuàng)建時(shí)間倒排序,可以構(gòu)建如下語句

LocalDate ld=LocalDate.of(2017,1,1);
Instant instant = Instant.from(ld);
Date date = Date.from(instant);

OrderDTOExample example=new OrderDTOExample();
//設(shè)置排序字段
example.setOrderByClause(" order by create_time desc ");

//創(chuàng)建查詢條件對(duì)象
example.createCriteria().andCustomerIdEqualTo(123456L).
andCreateTimeGreaterThan(date).
andPaymentAmtGreaterThan(300L);

//執(zhí)行查詢
return orderMapper.selectByExample(example);

一個(gè)動(dòng)態(tài)查詢不到一分鐘就寫出來了,沒有特殊需求,根本不用寫sql,簡單不簡單,驚喜不驚喜!!!

條件更新的使用方法類似,有興趣可以自己試下.

7. 解析Example及sqlmapper.xml

通常表中的每個(gè)字段會(huì)生成對(duì)應(yīng)Example.Criteria對(duì)象的12個(gè)方法

例如創(chuàng)建時(shí)間這個(gè)字段就生成了相等,不相等,區(qū)間內(nèi),區(qū)間外,大于,小于,空,非空等方法,基本覆蓋了常用的計(jì)算表達(dá)式

image.png

我們再來看看example對(duì)象的內(nèi)部結(jié)構(gòu)

image.png

我們可以看到在Example類中有三個(gè)內(nèi)部類Criteria,Criterion,GeneratedCriteria

  • Criteria是查詢條件類,繼承GeneratedCriteria類,,我們構(gòu)造查詢條件就是使用它的方法,它由Example對(duì)象的createCriteria()方法來創(chuàng)建.
  • Criterion是存儲(chǔ)字段的條件,例如id=5這個(gè)條件就是存儲(chǔ)在這個(gè)類對(duì)象的屬性中.
  • GeneratedCriteria是Criteria父類,Example對(duì)象映射表的每個(gè)字段的12個(gè)方法都屬于這個(gè)類,它保存一組Criterion對(duì)象.

我們來分析下動(dòng)查詢的sqlmapper.xml片段

  <select id="selectByExample" parameterType="org.dev.repo.dto.OrderDTOExample" resultMap="BaseResultMap">
    select
    <!--對(duì)應(yīng)Example中的distinct屬性 -->
    <if test="distinct">
      distinct
    </if>
    <!-- sql片段,order表字段 -->
    <include refid="Base_Column_List" />
    from order
    <if test="_parameter != null">
      <!--這里是重點(diǎn),sql片段,查詢條件-->
      <include refid="Example_Where_Clause" />
    </if>
    <!--對(duì)應(yīng)Example中的orderByClause屬性 -->
    <if test="orderByClause != null">
      order by ${orderByClause}
    </if>
  </select>

我們再看下sqlmapper.xml是如何構(gòu)造動(dòng)態(tài)查詢條件

<sql id="Example_Where_Clause">
    <where>
      <!-- 遍歷Example中的oredCriteria屬性中的元素Criteria,多個(gè)Criteria組成or條件 -->
      <foreach collection="oredCriteria" item="criteria" separator="or">
      <!-- 如果Example.Criteria對(duì)象存在一個(gè)或多個(gè)Criterion對(duì)象那么進(jìn)行遍歷,多個(gè)Criterion構(gòu)成and條件 -->
        <if test="criteria.valid">
          <trim prefix="(" prefixOverrides="and" suffix=")">
            <foreach collection="criteria.criteria" item="criterion">
              <choose>
                <!-- 無值條件,例如name is null -->
                <when test="criterion.noValue">
                  and ${criterion.condition}
                </when>
                <!-- 單值條件,例如 name='張三' -->
                <when test="criterion.singleValue">
                  and ${criterion.condition} #{criterion.value}
                </when>
                <!-- 雙值條件,對(duì)應(yīng)between -->
                <when test="criterion.betweenValue">
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <!-- 多值條件,對(duì)應(yīng)in -->
                <when test="criterion.listValue">
                  and ${criterion.condition}
                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>

通過以上sqlmapper.xml我們可以看出Example對(duì)象及它的內(nèi)部對(duì)象屬性在運(yùn)行時(shí)被mybatis框架循環(huán)遍歷解析成動(dòng)態(tài)sql語句,與我們手寫sql語句并無差別.

再舉個(gè)例子來收官

例如我們想查詢id等于123456的客戶在2017年11月11日的訂單

對(duì)應(yīng)的sql查詢條件是
customer_id=5 and create_time between '2017-11-11 00:00' and '2017-11-11 23:59'

對(duì)應(yīng)的程序代碼是
LocalDateTime start=LocalDateTime.of(2017,11,11,0,0);
Instant instant_start = Instant.from(start);
Date date_start = Date.from(instant_start);

LocalDateTime end=LocalDateTime.of(2017,11,11,23,59);
Instant instant_end = Instant.from(end);
Date date_end = Date.from(instant_end);

OrderDTOExample example=new OrderDTOExample();

example.createCriteria().andCustomerIdEqualTo(123456L).
andCreateTimeBetween(date_start,date_end);
        
return orderMapper.selectByExample(example);

在這段程序中andCustomerIdEqualTo(123456L)和andCreateTimeBetween(date_start,date_end),實(shí)際上創(chuàng)建了兩個(gè)Criterion對(duì)象,并被Example對(duì)象內(nèi)的Criteria對(duì)象持有.mybatis框架運(yùn)行時(shí)會(huì)按照sqlmapper.xml中定義的標(biāo)簽去遍歷解析Criteria對(duì)象(多個(gè)Criteria組成or關(guān)系),及Criteria對(duì)象中的所有Criterion對(duì)象(多個(gè)Criterion組成and關(guān)系),最終組成完整的sql語句,交給jdbc執(zhí)行.

8. 擴(kuò)展話題

  • 關(guān)于大文本字段的處理,需要自己實(shí)現(xiàn)org.apache.ibatis.type.BaseTypeHandler類,并配置到mybatis配置文件中才能正常使用,否則會(huì)出錯(cuò);

  • 批量寫入和更新,仍然可以使用dao提供的所有方法,只是Mapper對(duì)象需要從sqlSession中獲取,session需要設(shè)置成批量模式和禁止自動(dòng)提交

    public<T,M extends BaseMapper> long saveBatch(List<T> newRecord, Class<M> clazz) {

        Stopwatch sw=Stopwatch.createStarted();

        long rows=0;

        try(SqlSession sqlSession=sqlSessionFactory.openSession(ExecutorType.BATCH,false)){
            BaseMapper mapper=sqlSession.getMapper(clazz);
            rows=newRecord.stream().map(i->mapper.insertSelective(i)).collect(Collectors.counting());
            sqlSession.clearCache();
            sqlSession.commit();
        }

        sw.stop();
        log.info("batch insert ,total consume="+sw.elapsed(TimeUnit.MILLISECONDS)+"ms");
        return rows;
    }

9. 參考:

官網(wǎng)和教程http://www.mybatis.org/generator/index.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亮蒋,一起剝皮案震驚了整個(gè)濱河市今瀑,隨后出現(xiàn)的幾起案子懒震,更是在濱河造成了極大的恐慌鳄厌,老刑警劉巖又谋,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拼缝,死亡現(xiàn)場離奇詭異,居然都是意外死亡彰亥,警方通過查閱死者的電腦和手機(jī)咧七,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來任斋,“玉大人继阻,你說我怎么就攤上這事》峡幔” “怎么了瘟檩?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長澈蟆。 經(jīng)常有香客問我墨辛,道長,這世上最難降的妖魔是什么趴俘? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任睹簇,我火速辦了婚禮,結(jié)果婚禮上寥闪,老公的妹妹穿的比我還像新娘带膀。我一直安慰自己,他們只是感情好橙垢,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布垛叨。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嗽元。 梳的紋絲不亂的頭發(fā)上敛纲,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音剂癌,去河邊找鬼淤翔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛佩谷,可吹牛的內(nèi)容都是我干的旁壮。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼谐檀,長吁一口氣:“原來是場噩夢啊……” “哼抡谐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起桐猬,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤麦撵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后溃肪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體免胃,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年惫撰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了羔沙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡厨钻,死狀恐怖撬碟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情莉撇,我是刑警寧澤呢蛤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站棍郎,受9級(jí)特大地震影響其障,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涂佃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一励翼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辜荠,春花似錦汽抚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春惭蟋,著一層夾襖步出監(jiān)牢的瞬間苗桂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國打工告组, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留煤伟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓木缝,卻偏偏與公主長得像便锨,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子我碟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361