一、Mybatis的不足之处

Mybatis是一款优秀的及其灵活的持久层框架,通过XML配置并映射到Mapper接口为Service层提供基础数据操作入口。

这么优秀的框架竟然还有不足之处?

俗话说人无完人,因为Mybatis实在是太灵活了,灵活到每个Mapper接口都需要定制对应的XML,所以就会引发一些问题。

问题一:配置文件繁多

假如一个系统中DB中涉及100张表,我们就需要写100个Mapper接口,还没完,最可怕的是,我们要为这100个Mapper接口定制与之对应的100套XML。而每个Mapper都必不可少的需要增删改查功能,我们就要写100遍增删改查,作为高贵的Java开发工程师,这是不能容忍的,于是Mybatis Generator诞生了,然而又会引发另一个问题!

问题二:维护困难

我们使用Mybatis Generator解决了问题一,再多的文件生成就是了,简单粗暴,貌似解决了所有的问题,Mybatis完美了!

不要高兴的太早,在系统刚刚建立起来时,我们使用Mybatis Generator生成了一堆XML,在开发过程中,产品忽然提了一个新的需求,项目经理根据这个需求在某张表中增加或变动了一个字段,这时,我猜你的操作是这样:

  • 1、找到对应表的XML
  • 2、将该XML中自定义的一段标签复制出来,保存在本地
  • 3、使用Mybatis Generator重新生成该表的XML
  • 4、将之覆盖当前的XML
  • 5、将自定义的一段标签再粘贴进新的XML中

在这个过程中,如果我们在第2步时漏复制了一段标签,等整个操作完成之后,又别是一番滋味在心头~

问题三:编写XML困难

假如肝不错,问题二也是小CASE,那么问题又来了,我们如何在繁长的XML中去编写和修改我们的XML呢。

当我们打开要编辑的XML,映入眼帘的就是1000多行的XML,其中900行都是通用的增删改查操作,要新增一个标签,我们需要拉至文件底部编写新的数据操作,要更新一个标签,我们需要通过Ctrl + F寻找目标标签再进行修改。

如何避免这些问题呢?

如何让Mybatis增强通用性又不失灵活呢?

二、使用Ourbatis辅助Mybatis

Ourbatis是一款Mybatis开发增强工具,小巧简洁,项目地址:

  • Github:github.com/ainilili/ou…
  • Gitee:gitee.com/ainilili/ou…
  • Wiki:github.com/ainilili/ou…
  • Demo:github.com/ainilili/ou…

特性:

  • 1、简洁方便,可以让Mybatis无XML化开发。
  • 2、优雅解耦,通用和自定义的SQL标签完全隔离,让维护更加轻松。
  • 3、无侵入性,Mybatis和Ourbatis可同时使用,配置简洁。
  • 4、灵活可控,通用模板可自定义及扩展。
  • 5、部署快捷,只需要一个依赖,两个配置,即可直接运行。
  • 6、多数据源,在多数据源环境下也可以照常使用。

关于Ourbatis使用的一个小Demo

环境:

  • Spring Boot 2.0.5.RELEASE
  • Ourbatis 1.0.5
  • JAVA 8
  • Mysql

Spring Boot 2.0.5.RELEASE版本为例,在可以正常使用Mybatis的项目中,pom.xml添加如下依赖:

   <dependency><groupId>com.smallnico</groupId><artifactId>ourbatis-spring-boot-starter</artifactId><version>1.0.5</version></dependency>
复制代码

在配置文件中增加一下配置:

ourbatis.domain-locations=实体类所在包名
复制代码

接下来,Mapper接口只需要继承SimpleMapper即可:

import org.nico.ourbatis.domain.User;
public interface UserMapper extends SimpleMapper<User, Integer>{
}
复制代码

至此,一个使用Ourbatis的简单应用已经部署起来了,之后,你就可以使用一些Ourbatis默认的通用操作方法:

   public T selectById(K key);public T selectEntity(T condition);public List<T> selectList(T condition);public long selectCount(Object condition);public List<T> selectPage(Page<Object> page);default PageResult<T> selectPageResult(Page<Object> page){long total = selectCount(page.getEntity());List<T> results = null;if(total > 0) {results = selectPage(page);}return new PageResult<>(total, results);}public K selectId(T condition);public List<K> selectIds(T condition);public int insert(T entity);public int insertSelective(T entity);public int insertBatch(List<T> list);public int update(T entity);public int updateSelective(T entity);public int updateBatch(List<T> list);public int delete(T condition);public int deleteById(K key);public int deleteBatch(List<K> list);
复制代码

Mapper自定义方法

在很多场景中,我们使用以上的自带的通用方法远远不能满足我们的需求,我们往往需要额外扩展新的Mapper方法、XML标签,我们使用了Ourbatis之后该如何实现呢?

首先看一下我们的需求,在上述Demo中,我们在UserMapper中增加一个方法selectNameById

import org.nico.ourbatis.domain.User;
public interface UserMapper extends SimpleMapper<User, Integer>{public String selectNameById(Integer userId);
}
复制代码

和Mybatis一样,需要在resources资源目录下新建一个文件夹ourbatis-mappers,然后在其中新建一个XML文件,命名规则为:

DomainClassSimpleName + Mapper.xml
复制代码

其中DomainClassSimpleName就是我们实体类的类名,这里是为User,那么新建的XML名为UserMapper.xml

src/main/resources- ourbatis-mappers- UserMapper.xml
复制代码

之后,打开UserMapper.xml,开始编写Mapper中selectNameById方法对应的标签:

<select id="selectNameById" resultType="java.lang.String">select name from user where id = #{userId}
</select>
复制代码

注意,整个文件中只需要写标签就行了,其他的什么都不需要,这是为什么呢?深入之后你就会明白,这里先不多说!

接下来,就没有接下来了,可以直接使用selectNameById方法了。

深入了解Ourbatis

当服务启动的时候,Ourbatis首先会扫描ourbatis.domain-locations配置包下的所有实体类,将之映射为与之对应的表结构数据:

然后通过ourbatis.xml的渲染,生成一个又一个的XML文件,最后将之重新Build到Mybatis容器中!

整个过程分为两个核心点:

  • 1、映射实体类为元数据
  • 2、使用ourbatis.xml渲染元数据为XML文件

我会一一介绍之~

映射实体类为元数据

在映射时,我们要根据自己数据库字段命名的风格去调整映射规则,就需要在第1个核心点中去做处理,Ourbatis使用包装器来完成:

public interface Wrapper<T> {public String wrapping(T value);
}
复制代码

对于需要映射的字段,如表名表字段名,它们都将会经过一个包装器链条的处理之后再投入到ourbatis.xml中做渲染,这样就使得我们可以自定义包装器出更换映射的字段格式,具体方式可以参考官方Wiki:Wrapper包装器

使用ourbatis.xml渲染元数据为XML文件

而在于第2个核心点中,Ourbatis通过自定义标签做模板渲染,我们可以先看一下官方默认的ourbatis.xml内部构造:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="@{mapperClassName}"><resultMap id="BaseResultMap" type="@{domainClassName}"><ourbatis:foreach list="primaryColumns" var="elem"><id column="@{elem.jdbcName}" property="@{elem.javaName}" /></ourbatis:foreach><ourbatis:foreach list="normalColumns" var="elem"><result column="@{elem.jdbcName}" property="@{elem.javaName}" /></ourbatis:foreach></resultMap><sql id="Base_Column_List"><ourbatis:foreach list="allColumns" var="elem"split=",">`@{elem.jdbcName}`</ourbatis:foreach></sql><select id="selectById" parameterType="java.lang.Object"resultMap="BaseResultMap">select<include refid="Base_Column_List" />from @{tableName}where 1 = 1<ourbatis:foreach list="primaryColumns" var="elem">and `@{elem.jdbcName}` = #{@{elem.javaName}}</ourbatis:foreach></select><select id="selectEntity" parameterType="@{domainClassName}"resultMap="BaseResultMap">select<include refid="Base_Column_List" />from @{tableName}where 1 = 1<ourbatis:foreach list="allColumns" var="elem"><if test="@{elem.javaName} != null">and `@{elem.jdbcName}` = #{@{elem.javaName}}</if></ourbatis:foreach>limit 1</select><select id="selectCount" parameterType="@{domainClassName}"resultType="long">select count(0)from @{tableName}where 1 = 1<ourbatis:foreach list="allColumns" var="elem"><if test="@{elem.javaName} != null">and `@{elem.jdbcName}` = #{@{elem.javaName}}</if></ourbatis:foreach>limit 1</select><select id="selectPage"parameterType="org.nico.ourbatis.entity.Page"resultMap="BaseResultMap">select<include refid="Base_Column_List" />from @{tableName}where 1 = 1<if test="entity != null"><ourbatis:foreach list="allColumns" var="elem"><if test="entity.@{elem.javaName} != null">and `@{elem.jdbcName}` = #{entity.@{elem.javaName}}</if></ourbatis:foreach></if><if test="orderBy != null">order by ${orderBy}</if><if test="start != null and end != null">limit ${start},${end}</if></select><select id="selectList" parameterType="@{domainClassName}"resultMap="BaseResultMap">select<include refid="Base_Column_List" />from @{tableName}where 1 = 1<ourbatis:foreach list="allColumns" var="elem"><if test="@{elem.javaName} != null">and `@{elem.jdbcName}` = #{@{elem.javaName}}</if></ourbatis:foreach></select><select id="selectId" parameterType="@{domainClassName}"resultType="java.lang.Object">select<ourbatis:foreach list="primaryColumns" var="elem"split=",">`@{elem.jdbcName}`</ourbatis:foreach>from @{tableName}where 1 = 1<ourbatis:foreach list="allColumns" var="elem"><if test="@{elem.javaName} != null">and `@{elem.jdbcName}` = #{@{elem.javaName}}</if></ourbatis:foreach>limit 1</select><select id="selectIds" parameterType="@{domainClassName}"resultType="java.lang.Object">select<ourbatis:foreach list="primaryColumns" var="elem"split=",">`@{elem.jdbcName}`</ourbatis:foreach>from @{tableName}where 1 = 1<ourbatis:foreach list="normalColumns" var="elem"><if test="@{elem.javaName} != null">and `@{elem.jdbcName}` = #{@{elem.javaName}}</if></ourbatis:foreach></select><delete id="deleteById" parameterType="java.lang.Object">deletefrom @{tableName}where 1=1<ourbatis:foreach list="primaryColumns" var="elem">and `@{elem.jdbcName}` = #{@{elem.javaName}}</ourbatis:foreach></delete><insert id="insert" keyProperty="@{primaryColumns.0.jdbcName}"useGeneratedKeys="true" parameterType="@{domainClassName}">insert into @{tableName}(<include refid="Base_Column_List" />)values (<ourbatis:foreach list="allColumns" var="elem"split=",">#{@{elem.javaName}}</ourbatis:foreach>)</insert><insert id="insertSelective"keyProperty="@{primaryColumns.0.jdbcName}" useGeneratedKeys="true"parameterType="@{domainClassName}">insert into @{tableName}(<ourbatis:foreach list="primaryColumns" var="elem"split=",">`@{elem.jdbcName}`</ourbatis:foreach><ourbatis:foreach list="normalColumns" var="elem"><if test="@{elem.javaName} != null">,`@{elem.jdbcName}`</if></ourbatis:foreach>)values (<ourbatis:foreach list="primaryColumns" var="elem">#{@{elem.javaName}}</ourbatis:foreach><ourbatis:foreach list="normalColumns" var="elem"><if test="@{elem.javaName} != null">, #{@{elem.javaName}}</if></ourbatis:foreach>)</insert><insert id="insertBatch"keyProperty="@{primaryColumns.0.jdbcName}" useGeneratedKeys="true"parameterType="java.util.List">insert into @{tableName}(<include refid="Base_Column_List" />)values<foreach collection="list" index="index" item="item"separator=",">(<ourbatis:foreach list="allColumns" var="elem"split=",">#{item.@{elem.javaName}}</ourbatis:foreach>)</foreach></insert><update id="update" parameterType="@{domainClassName}">update @{tableName}<set><ourbatis:foreach list="normalColumns" var="elem"split=",">`@{elem.jdbcName}` = #{@{elem.javaName}}</ourbatis:foreach></set>where 1=1<ourbatis:foreach list="primaryColumns" var="elem">and `@{elem.jdbcName}` = #{@{elem.javaName}}</ourbatis:foreach></update><update id="updateSelective" parameterType="@{domainClassName}">update @{tableName}<set><ourbatis:foreach list="primaryColumns" var="elem"split=",">`@{elem.jdbcName}` = #{@{elem.javaName}}</ourbatis:foreach><ourbatis:foreach list="normalColumns" var="elem"><if test="@{elem.javaName} != null">,`@{elem.jdbcName}` = #{@{elem.javaName}}</if></ourbatis:foreach></set>where 1=1<ourbatis:foreach list="primaryColumns" var="elem">and `@{elem.jdbcName}` = #{@{elem.javaName}}</ourbatis:foreach></update><update id="updateBatch" parameterType="java.util.List"><foreach collection="list" index="index" item="item"separator=";">update @{tableName}<set><ourbatis:foreach list="normalColumns" var="elem"split=",">`@{elem.jdbcName}` = #{item.@{elem.javaName}}</ourbatis:foreach></set>where 1=1<ourbatis:foreach list="primaryColumns" var="elem">and `@{elem.jdbcName}` = #{item.@{elem.javaName}}</ourbatis:foreach></foreach></update><delete id="deleteBatch" parameterType="java.util.List">delete from @{tableName} where @{primaryColumns.0.jdbcName} in<foreach close=")" collection="list" index="index" item="item"open="(" separator=",">#{item}</foreach></delete><delete id="delete" parameterType="@{domainClassName}">delete from @{tableName} where 1 = 1<ourbatis:foreach list="allColumns" var="elem"><if test="@{elem.javaName} != null">and `@{elem.jdbcName}` = #{@{elem.javaName}}</if></ourbatis:foreach></delete><ourbatis:ref path="classpath:ourbatis-mappers/@{domainSimpleClassName}Mapper.xml" />
</mapper>
复制代码

可以看出来,ourbatis.xml内容类似于原生的Mybatis的XML,不同的是,有两个陌生的标签:

  • ourbatis:foreach 对元数据中的列表进行循环渲染
  • ourbatis:ref 引入外界文件内容

这是Ourbatis中独有的标签,Ourbatis也提供着对应的入口让我们去自定义标签:

Class: org.nico.ourbatis.Ourbatis
Field:
public static final Map<String, AssistAdapter> ASSIST_ADAPTERS = new HashMap<String, AssistAdapter>(){private static final long serialVersionUID = 1L;{put("ourbatis:foreach", new ForeachAdapter());put("ourbatis:ref", new RefAdapter());}};
复制代码

我们可以修改org.nico.ourbatis.Ourbatis类中的静态参数ASSIST_ADAPTERS去删除、更新和添加自定义标签,需要实现一个标签适配器,我们可以看一下最简单的RefAdapter适配器的实现:

public class RefAdapter extends AssistAdapter{@Overridepublic String adapter(Map<String, Object> datas, NoelRender render, Document document) {String path = render.rending(datas, document.getParameter("path"), "domainSimpleClassName");String result =  StreamUtils.convertToString(path.replaceAll("classpath:", ""));return result == null ? "" : result.trim();}
}
复制代码

Ourbatis中只定义了上述两个自定义标签已足够满足需求,通过foreach标签,将元数据中的集合遍历渲染,通过ref标签引入外界资源,也就是我们之前所说的对Mapper接口中方法的扩展!

<ourbatis:ref path="classpath:ourbatis-mappers/@{domainSimpleClassName}Mapper.xml" />
复制代码

其中的path就是当前项目classpath路径的相对路径,而@{domainSimpleClassName}就代表着实体类的类名,更多的系统参数可以参考Wiki:元数据映射

通过这种模板渲染的机制,Ourbatis是相当灵活的,我们不仅可以通过引入外部文件进行扩展,当我们需要添加或修改通用方法时,我们可以可以自定义ourbatis.xml的内容,如何做到呢?复制一份将之放在资源目录下就可以了!

看到这里,相信大家已经知道Ourbatis的基本原理已经使用方式,我就再次不多说了,更多细节可以去官方Wiki中阅读:Ourbtis Wiki

使Mybatis开发变得更加轻松的增强工具 — Ourbatis相关推荐

  1. TPT又一次重量级更新——TPT16使嵌入式测试变得更加轻松

    关于TPT TPT是一款基于模型的动态自动化测试工具,它提供了一套高效的测试建模方法,覆盖MiL.SiL.PiL.HiL.ViL等全阶段的测试过程.我们可以使用TPT将测试执行到测试报告生成的所有步骤 ...

  2. android 好的开发工具,使Android开发方便快捷的8个好工具

    Android是第二个最流行的用于 智能手机和平板电脑 的操作系统.这里有8个最好的 Android工具以许多不同的方式 帮助开发人员 ,例如 - SDK和AVD管理器,android ADT,and ...

  3. 让iOS开发变得更有效率-分类、工具类

    转自:xiao公子的简书(@Lee枭枭) 在工作中整理的一些分类与工具类,分享给大家.这些工具类可以减少项目中的代码量,让代码变得更简洁,可以大大的提升项目的效率,直接拖到项目中使用即可. 下载地址: ...

  4. MacBook触控板也可以变得更强大——手势增强工具 Better and better

    提到 Mac触控板可谓是0差评的产品,我今天说的这个工具就是苹果电脑的一个非常好用且非常有趣的软件工具-- Better and Better.众所周知,苹果电脑的原生手势已经有数十种,但安装这种软件 ...

  5. java会变得更简单吗_spring 第一篇(1-1):让java开发变得更简单(下)

    切面(aspects)应用 DI能够让你的软件组件间保持松耦合,而面向切面编程(AOP)能够让你捕获到在整个应用中可重用的组件功能.在软件系统中,AOP通常被定义为提升关注点分离的一个技术.系统由很多 ...

  6. 十款让 Web 前端开发人员更轻松的实用工具

    这篇文章介绍十款让 Web 前端开发人员生活更轻松的实用工具.每个 Web 开发人员都有自己的工具箱,这样工作中碰到的每个问题都有一个好的解决方案供选择. 对于每一项工作,开发人员需要特定的辅助工具, ...

  7. 你真的会用iPad吗,如何使iPad秒变生产力工具?在iPad上用vscode写代码搞开发

    目录 前言 视频教程 1. 本地环境配置 2. 内网穿透 2.1 安装cpolar内网穿透(支持一键自动安装脚本) 2.2 创建HTTP隧道 3. 测试远程访问 4. 配置固定二级子域名 4.1 保留 ...

  8. 如何开发一个hexo主题_如何确定一个强烈的主题可以使产品开发更有效

    如何开发一个hexo主题 by Cameron Jenkinson 卡梅伦·詹金森(Cameron Jenkinson) 如何确定一个强烈的主题可以使产品开发更有效 (How identifying ...

  9. 【MyBatis框架】Mybatis开发dao方法第二部分

    下面来继续讨论mybatis开发Dao的方法 我们前面使用原始的Dao开发方法,发现了许多弊端,我们下面使用mapper代理来写Dao方法. 1.mapper代理方法(程序员只需要mapper接口(相 ...

最新文章

  1. 一个阿里P7的自白:一念之差,我差点转了产品
  2. [Spring 深度解析]第2章 Spring基础
  3. 几款让你爱不释手的语音转文字软件
  4. java11开源中国,Java 11 正式发布!
  5. 【tomcat】tomcat部署项目实验
  6. CCNA-第一篇-基础入门概念
  7. JNI开发笔记(六)--一种更规范的so库生成方法
  8. React组件生命周期-正确执行运行阶段的函数
  9. 信息的存储与值的计算
  10. SQL函数学习 之 DENSE_RANK() OVER (PARTITION BY col2 ORDER BY col3 DESC) AS seq
  11. IE7 绝对定位z-index问题
  12. Luogu P2617 Dynamic Rankings
  13. Android简单实现高德地图显示及定位
  14. Nessus最新版8.15安装教程
  15. 堪比端游!欧美十大RPG网页游戏推荐
  16. 大会没看够?2021 Google 开发者大会总结看这里
  17. CAN总线电容过大的有效解决方法
  18. 字节跳动面试题汇总 -- C++后端(含答案)
  19. 一篇高中生都能看懂的MySQL入门博客(长文)
  20. JavaScript给网页添加水印

热门文章

  1. 自定义ActionSheetView
  2. 《机器学习实战》第5章 随机梯度上升算法
  3. [unreal4入门系列之九] UE4创建空白关卡并添加碰撞体
  4. python中torch模块下载,Python qtorch包_程序模块 - PyPI - Python中文网
  5. 屏显有啥硬科技可卷?让现在电视厂商这么拼
  6. MIT新研究:43%算法改进速度超摩尔定律,解决超大规模问题,算法比硬件更有用...
  7. Nature:万物皆可“编程”,结构材料也能实现数据存储,华人科学家一作
  8. session、flask session知识的相关收集
  9. zabbix 3.4 ubuntu 16 用腾讯企业邮箱作为告警邮箱
  10. android MIPI屏 导航栏丢失