一, 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: https://github.com/ainilili/ourbatis
- Gitee: https://gitee.com/ainilili/ourbatis
- Wiki: https://github.com/ainilili/ourbatis/wiki
- Demo: https://github.com/ainilili/ourbatis-simple
特性:
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
- <select id="selectNameById" resultType="java.lang.String">
- select name from user where id = #{userId}
- </select>
- public interface Wrapper<T> {
- public String wrapping(T value);
- }
- <?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">
- delete
- from @{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>
- 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());
- }
- };
- public class RefAdapter extends AssistAdapter{
- @Override
- public 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();
- }
- }
来源: https://juejin.im/post/5bc8329de51d450e4a1c2521