mybatis是数据库开发最常用的ORM框架,目前有很多码农仍然采用传统的手工编写sql语句的方式来开发,
经常出现的问题是工作量大,字段名拼写错误,排错困难,特别是做老代码维护,更是巨大的陷阱,雷同语句多,谁都不敢动,需求来了只能新增语句(这也符合开闭原则,哈哈),面对系统不断增加的熵值,你该如何解呢?
我给大家介绍一款生产力工具mybatis generator.它是一款mybatis 代码生成器,可以自动生成dto,dao及sqlmapper.xml,其中的亮点是自动生成可编程的动态查询构造器--Example,采用这款工具基本可以应付90%的开发场景,都可以不用写sql语句.更值得一提的是
使用非常简单!!!
下面简要介绍下这款工具
本文采用Maven插件mybatis-generator-maven-plugin来配置MyBatis Generator
准备条件:maven工程,mysql库
1. 增加maven依赖
mysql
mysql-connector-java
5.1.43
org.mybatis
mybatis
3.4.0
org.mybatis
mybatis-spring
1.3.0
2. 增加maven插件
org.mybatis.generator
mybatis-generator-maven-plugin
1.3.2
true
true
src/main/resources/db-init/generatorConfig.xml
mysql
mysql-connector-java
${mysql.version}
3. 编辑mybatis-generator配置文件
4. 生成dto,dao和mapper.xml文件
双击运行mybatis-generator.generate插件会自动生成dto,dao,example和mapper.xml文件
也可以通过运行mvn mybatis-generator:generate命令来生成代码
生成文件如图所示
我创建了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 selectByExampleWithBLOBs(OrderDTOExample example);
//条件查询,返回结果不包括text等大字段
List 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个方法
例如创建时间这个字段就生成了相等,不相等,区间内,区间外,大于,小于,空,非空等方法,基本覆盖了常用的计算表达式
我们再来看看example对象的内部结构
我们可以看到在Example类中有三个内部类Criteria,Criterion,GeneratedCriteria
- Criteria是查询条件类,继承GeneratedCriteria类,,我们构造查询条件就是使用它的方法,它由Example对象的createCriteria()方法来创建.
- Criterion是存储字段的条件,例如id=5这个条件就是存储在这个类对象的属性中.
- GeneratedCriteria是Criteria父类,Example对象映射表的每个字段的12个方法都属于这个类,它保存一组Criterion对象.
我们来分析下动查询的sqlmapper.xml片段
我们再看下sqlmapper.xml是如何构造动态查询条件
and ${criterion.condition}
and ${criterion.condition} #{criterion.value}
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
and ${criterion.condition}
#{listItem}
通过以上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 long saveBatch(List newRecord, Class 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