Mybatis生产力工具 mybatis generator

mybatis是数据库开发最常用的ORM框架,目前有很多码农仍然采用传统的手工编写sql语句的方式来开发,
经常出现的问题是工作量大,字段名拼写错误,排错困难,特别是做老代码维护,更是巨大的陷阱,雷同语句多,谁都不敢动,需求来了只能新增语句(这也符合开闭原则,哈哈),面对系统不断增加的熵值,你该如何解呢?

我给大家介绍一款生产力工具mybatis generator.它是一款mybatis 代码生成器,可以自动生成dto,dao及sqlmapper.xml,其中的亮点是自动生成可编程的动态查询构造器--Example,采用这款工具基本可以应付90%的开发场景,都可以不用写sql语句.更值得一提的是

使用非常简单!!!

下面简要介绍下这款工具
本文采用Maven插件mybatis-generator-maven-plugin来配置MyBatis Generator

准备条件: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>
                <!-- 数据库驱动  -->
                <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>
            <!--目标数据库配置-->
            <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类型,避免数据库中number等类型字段 -->
            <javaTypeResolver>
                  <property name="forceBigDecimals" value="false"/>
            </javaTypeResolver>
            <!-- 生成model模型,对应的包,存放位置可以指定具体的路径,如/ProjectName/src,也可以使用MAVEN来自动生成 -->
            <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>
            <!--对应的xml mapper文件  -->
            <sqlMapGenerator targetPackage="org.test.repo.dao.mapper"  targetProject="src/main/java">
                <property name="enableSubPackages" value="false"/>
            </sqlMapGenerator>
            <!-- 对应的dao接口 -->
            <javaClientGenerator type="XMLMAPPER" targetPackage="org.test.repo.dao" targetProject="src/main/java">
                <property name="enableSubPackages" value="false"/>
            </javaClientGenerator>
              <!--定义需要操作的表及对应的DTO名称-->
            <table tableName="order" domainObjectName="OrderDTO"/>
            <table tableName="order_item" domainObjectName="OrderItemDTO"/>
        </context>
</generatorConfiguration>

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

image.png

双击运行mybatis-generator.generate插件会自动生成dto,dao,example和mapper.xml文件

也可以通过运行mvn mybatis-generator:generate命令来生成代码

生成文件如图所示

image.png

我创建了order,order_item两张表做例子,红色圈自动生成的动态查询构造器Example,每张表都生成一个对应的Example类.

5. mapper类介绍

mapper也就是数据库访问类dao,我们看下都有什么内容

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 {
    //通过条件计数
    int countByExample(OrderDTOExample example);
    //通过条件删除
    int deleteByExample(OrderDTOExample example);
    //通过主键删除
    int deleteByPrimaryKey(Long id);
    //插入DTO
    int insert(OrderDTO record);
    //dto非空字段插入
    int insertSelective(OrderDTO record);
    //条件查询,返回结果包括text等大字段
    List<OrderDTO> selectByExampleWithBLOBs(OrderDTOExample example);
    //条件查询,返回结果不包括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,我们看出基本我们常用的创建,修改,查询和删除都有了.

我们来看下几个常用方法:

  • 最常用的动态查询方法selectByExample(OrderDTOExample example),简单通过构造不同的example对象就可以实现.
  • 后缀为Selective的插入和更新方法,只处理dto对象的非空字段,例如经常我们只想更新某些记录的部分字段,那么updateByExampleSelective方法最合适不过.
  • 后缀为WithBLOBs的方法是对大字段的处理方法,与常规处理方法分离,也体现了作者的周到.

6. 动态查询例子

我们看下如何使用Example来进行动态查询

例如:我们要查询id为123456用户在2017年1月1日后金额在300元以上的所有订单,并按照创建时间倒排序,可以构建如下语句

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

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

//创建查询条件对象
example.createCriteria().andCustomerIdEqualTo(123456L).
andCreateTimeGreaterThan(date).
andPaymentAmtGreaterThan(300L);

//执行查询
return orderMapper.selectByExample(example);

一个动态查询不到一分钟就写出来了,没有特殊需求,根本不用写sql,简单不简单,惊喜不惊喜!!!

条件更新的使用方法类似,有兴趣可以自己试下.

7. 解析Example及sqlmapper.xml

通常表中的每个字段会生成对应Example.Criteria对象的12个方法

例如创建时间这个字段就生成了相等,不相等,区间内,区间外,大于,小于,空,非空等方法,基本覆盖了常用的计算表达式

image.png

我们再来看看example对象的内部结构

image.png

我们可以看到在Example类中有三个内部类Criteria,Criterion,GeneratedCriteria

  • Criteria是查询条件类,继承GeneratedCriteria类,,我们构造查询条件就是使用它的方法,它由Example对象的createCriteria()方法来创建.
  • Criterion是存储字段的条件,例如id=5这个条件就是存储在这个类对象的属性中.
  • GeneratedCriteria是Criteria父类,Example对象映射表的每个字段的12个方法都属于这个类,它保存一组Criterion对象.

我们来分析下动查询的sqlmapper.xml片段

  <select id="selectByExample" parameterType="org.dev.repo.dto.OrderDTOExample" resultMap="BaseResultMap">
    select
    <!--对应Example中的distinct属性 -->
    <if test="distinct">
      distinct
    </if>
    <!-- sql片段,order表字段 -->
    <include refid="Base_Column_List" />
    from order
    <if test="_parameter != null">
      <!--这里是重点,sql片段,查询条件-->
      <include refid="Example_Where_Clause" />
    </if>
    <!--对应Example中的orderByClause属性 -->
    <if test="orderByClause != null">
      order by ${orderByClause}
    </if>
  </select>

我们再看下sqlmapper.xml是如何构造动态查询条件

<sql id="Example_Where_Clause">
    <where>
      <!-- 遍历Example中的oredCriteria属性中的元素Criteria,多个Criteria组成or条件 -->
      <foreach collection="oredCriteria" item="criteria" separator="or">
      <!-- 如果Example.Criteria对象存在一个或多个Criterion对象那么进行遍历,多个Criterion构成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>
                <!-- 双值条件,对应between -->
                <when test="criterion.betweenValue">
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <!-- 多值条件,对应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对象及它的内部对象属性在运行时被mybatis框架循环遍历解析成动态sql语句,与我们手写sql语句并无差别.

再举个例子来收官

例如我们想查询id等于123456的客户在2017年11月11日的订单

对应的sql查询条件是
customer_id=5 and create_time between '2017-11-11 00:00' and '2017-11-11 23:59'

对应的程序代码是
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),实际上创建了两个Criterion对象,并被Example对象内的Criteria对象持有.mybatis框架运行时会按照sqlmapper.xml中定义的标签去遍历解析Criteria对象(多个Criteria组成or关系),及Criteria对象中的所有Criterion对象(多个Criterion组成and关系),最终组成完整的sql语句,交给jdbc执行.

8. 扩展话题

  • 关于大文本字段的处理,需要自己实现org.apache.ibatis.type.BaseTypeHandler类,并配置到mybatis配置文件中才能正常使用,否则会出错;

  • 批量写入和更新,仍然可以使用dao提供的所有方法,只是Mapper对象需要从sqlSession中获取,session需要设置成批量模式和禁止自动提交

    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. 参考:

官网和教程http://www.mybatis.org/generator/index.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,547评论 4 374
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,787评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,175评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,752评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,169评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,056评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,195评论 2 321
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,980评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,724评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,872评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,344评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,667评论 3 264
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,379评论 3 245
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,202评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,992评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,189评论 2 286
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,987评论 2 279