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{ 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> <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的。
利用以上原理,我写了一个代码生成工具,读取数据库模型,并基于velocity模板,生成代码。
可以命令行形式运行,也可以作为IDEA的插件运行。
项目地址: https://github.com/kongxiangx...
工具下载地址: https://github.com/kongxiangx...
源码里提供了一个示例DEMO,里面包含了MyBatis的代码生成模板,支持如下特性:
/path/to/jasmine-[version]/bin/jasmine /path/to/jasmine-src/demo/jasmine.properties
如果一切正常,会在demo下看到生成出来的文件