背景

MyBatis的历史可谓久远了,码农们也在用着各式各样的代码生成工具。然而这些工具大部分都有一个缺点,那就是只能一次性生成文件。如果我们期间在生成的文件里做了修改,再次生成时,很多工具会覆盖我们的修改。

为什么会在生成文件后进行修改呢? 因为工具只会帮我们生成通用的数据库访问方法(比如只生成基本的CURD操作),我们不可避免的要根据实际的业务需要,添加其他的操作方法。

同时,数据库也不是设计完之后就一成不变的了,我们也可能在开发的过程中,调整已经建好的表结构。这个时候问题就来了,利用工具再生成一次?那就要人肉合并修改;手动添加更改后的字段进去? 太多了怕遗漏。

所以,我们需要找到一个方法,解决这个痛点。

原理

熟悉.NET的同学可能知道,大名鼎鼎Visual Studio也会帮开发人员生成很多代码,比如asp.net中的aspx的后台代码,它是如何保证被工具所生成的代码片段和开发人员自己写的代码片段不冲突的呢?它实际上用到了C#的分部类(partial)特性。

简单来说,分部类,就是把一个类的代码,放到多个文件中去写,C#编译器负责把他们编译到一个类中。有了这个特性,代码生成器就只专注他负责的partial文件就可以了,开发人员的代码写到另外一个partial文件中,当年用partial + T4,写了很多代码生成模板,屡试不爽。

但我们的JAVA不支持这个神器啊(这里说句题外话,几年前我从C#转到JAVA的时候,就感觉C#在语言层面比JAVA好太多了,现在好几年没碰C#了,不知道它又先进到什么程度了),怎么办呢?

只有用不是办法的办法了,那就是继承。实体类、Repository接口,用继承的方式,把工具生成的代码和预留给开发人员人肉的代码,分割到两个文件中。

但mapper.xml怎么办?这个MyBatis帮我们想好了(赞一个),利用namespace即可做到。只要namespace指向同一个Repository接口,不论是不是在同一个xml文件里,MyBatis都可以正确找到。

例如我们有一个Repository是这么定义的:

public interface UserRepository{
//aaa
User selectByPrimaryKey(@Param("id") Long id); User selectByAccount(@Param("account") String account);
}

那么以下的两个mapper.xml结合起来是完全可用的

UserMapper1.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="com.abc.demo.repository.UserRepository">
<sql id="TableName">
jm_user
</sql>
<!--asdfdsfsdf-->
<sql id="BaseColumnList">
`id`, `account`, `email`, `is_active`
</sql> <resultMap id="BaseResultMap" type="com.abc.demo.entity.User" autoMapping="false">
<result column="id" property="id" jdbcType="BIGINT"/>
<result column="account" property="account" jdbcType="VARCHAR"/>
<result column="email" property="email" jdbcType="VARCHAR"/>
<result column="is_active" property="isActive" jdbcType="BIT"/>
</resultMap>
<select id="selectByPrimaryKey" resultMap="BaseResultMap">
select
<include refid="BaseColumnList"/>
from
<include refid="TableName"/>
where
`id` = #{id}
</select>
</mapper>

UserMapper2.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="com.abc.demo.repository.UserRepository">
<select id="selectByAccount" resultMap="BaseResultMap">
select
<include refid="BaseColumnList"/>
from
<include refid="TableName"/>
where
`account` = #{account}
</select>
</mapper>

你看,UserMapper2.xml中只定义了selectByAccount方法,BaseColumnList、TableName、BaseResultMap都没有重新定义,可以直接用UserMapper1.xml的。

jasmine —— 基于数据库模型和velocity模板的代码生成工具

利用以上原理,我写了一个代码生成工具,读取数据库模型,并基于velocity模板,生成代码。

可以命令行形式运行,也可以作为IDEA的插件运行。

项目地址:https://github.com/kongxiangxin/jasmine

工具下载地址:https://github.com/kongxiangxin/jasmine/releases

源码里提供了一个示例DEMO,里面包含了MyBatis的代码生成模板,支持如下特性:

  1. 一键生成实体类、MyBatis Repository、MyBatis Mapper
  2. 实体类、MyBatis Repository和MyBatis Mapper均利用继承策略,划分出XXXX和XXXXBase两个文件,其中XXXX如果文件存在则不覆盖,XXXXBase每次生成都会覆盖。如果在生成后你需要做一些代码上的调整,请在XXXX文件中修改,而不要在XXXXBase中修改。这样做的好处是一旦我们的表结构发生变化需要重新生成时,不会覆盖您手动改过的代码。
  3. 如果表存在is_deleted字段,生成的delete方法是逻辑删除而不是物理删除。
  4. 如果表存在record_version字段,update语句带有乐观锁,即update .... set record_version=record_version + 1 where .... and record_version=#{record_version}
  5. 如果表存在create_time,insert语句这一列的值是now()
  6. 如果表存在update_time, insert和update语句这一列的值是now()

Quick Start

  1. 去releases页面,下载最新的jasmine-[version].zip,解压。
  2. clone源码至本地,根据实际情况,修改demo/jasmine.properties中jdbc相关的配置(主要是数据库连接配置)
  3. 执行以下命令:
/path/to/jasmine-[version]/bin/jasmine /path/to/jasmine-src/demo/jasmine.properties

如果一切正常,会在demo下看到生成出来的文件

拒绝一次性买卖:MyBatis的mapper和repository可重复生成工具的更多相关文章

  1. Spring和mybatis整合 org.mybatis.spring.mapper.MapperScannerConfigurer

    在springmvc与mybatis整合时,需要对每一个mapper定义对应的一个MapperFactoryBean,可以使用MapperScannerConfigurer自动扫描mapper,然后自 ...

  2. org.mybatis.spring.mapper.MapperScannerConfigurer 类作用

    1. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property nam ...

  3. MyBatis的Mapper文件的foreach标签详解

    MyBatis的Mapper文件的foreach标签用来迭代用户传递过来的Lise或者Array,让后根据迭代来拼凑或者批量处理数据.如:使用foreach来拼接in子语句. 在学习MyBatis M ...

  4. Spring+SpringMVC+Mybatis大整合(SpringMVC采用REST风格、mybatis采用Mapper代理)

    整体目录结构: 其中包下全部是采用mybatis自动生成工具生成. mybatis自动生成文件 <?xml version="1.0" encoding="UTF- ...

  5. 【SSM 8】spring集成Mybatis通用Mapper

    上篇博客中介绍了关于Mybatis底层封装的思路问题,那么这篇博客,就介绍一下怎么引入通用的mapper插件. 备注:本项目通过maven管理 关键版本说明: spring:4.1.3.RELEASE ...

  6. 关于mybatis 的mapper namespace 作用及解析

    因为语言惯性,大部分的namespace 在语言级别*来说是作为一种限定性标识来用,起到唯一或一类的标识.来看看语言(以PHP语言为例)上的namespace的作用实例 一.namespace 在PH ...

  7. Mybatis的mapper接口接受的参数类型

    最近项目用到了Mybatis,学一下记下来. Mybatis的Mapper文件中的select.insert.update.delete元素中有一个parameterType属性,用于对应的mappe ...

  8. Spring Boot MyBatis 通用Mapper插件集成

    Mybatis在使用过程中需要三个东西,每张表对应一个XXMapper.java接口文件,每张表对应一个XXMapper.xml文件,每张表对应一个Entity的Java文件.   其中XXMappe ...

  9. 关于使用mybatis中mapper instrances,通过session另一种操作方式

    String resource = "mybatis-config.xml"; InputStream inputStream = null; try { // 获取SqlSess ...

随机推荐

  1. 一文了解java异常机制

    1.异常的概述 1.1什么是异常? 异常:程序在运行过程中发生由于外部问题导致的程序异常事件,发生的异常会中断程序的运行.(在Java等面向对象的编程语言中)异常本身是一个对象,产生异常就是产生了一个 ...

  2. Vector使用方法简单整理

    使用vector,需要引用vector库: #include<vector> 首先,创建一个可以容纳int的vector变量——arr: vector<int> arr; 接着 ...

  3. while 的循环遍历 分享心得

    while 基本循环体 1.while while 条件: 循环体 2.while else while 条件: 循环体 else:#如果while条件结果为假 不执行循环体 直接执行else 代码块 ...

  4. 【资源共享】eBook分享大集合

    传送门:[GitHub] 欢迎各位指点,要是能补充更是感激不尽. 主要以IT领域经典书籍收藏,以备不时之需,不一定都能看完,权且当做收藏好玩. [x] 表示文件大小超过100M(LFS). 服务器系统 ...

  5. 人脸识别开发套件RJ45、继电器、OTG、RS232接口说明

          人脸识别开发套件RJ45.继电器.OTG.RS232接口说明 接口说明 D801A 人脸抓拍识别一体机是一款高性能.高可靠性的人脸识别类产品.依托深度学习算法扩展人脸库数量,准确率更高,支 ...

  6. json模块和pickle模块

    json模块和pickle模块 一.json模块 作用:用python写了一个程序,用java写了一门程序,这两个程序需要数据之间交流,就产生了一种多种语言通用的数据类型,json串. 序列化:把对象 ...

  7. 思科ACS5.8最新搭建教程-亲测可用

    1.需要准备的相关软件 ACS5.8安装包:http://ouo.io/MWB0R ACS5.8破解包:http://ouo.io/FaiGgj Centos7:下载地址(破解时需要):http:// ...

  8. 如何编写高质量的 JS 函数(1) -- 敲山震虎篇

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/7lCK9cHmunvYlbm7Xi7JxQ作者:杨昆 一千个读者,有一千个哈姆雷特. 此系列文 ...

  9. Java 迭代接口:Iterator、ListIterator 和 Spliterator

    1. 简介 当我们使用 for 或 while 循环来遍历一个集合的元素,Iterator 允许我们不用担心索引位置,甚至让我们不仅仅是遍历一个集合,同时还可以改变它.例如,你如果要删除循环中的元素, ...

  10. 外观/门面模式(Facade)

    2015/4/28 外观/门面模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. #include <vector> ...