spring boot2项目基础之mybatis-plus

本文介绍了在Spring Boot2项目中使用MyBatis-Plus,针对MyBatis-Plus的代码生成器进行扩展,支持枚举字段,并详细阐述了添加依赖、配置生成策略、自定义枚举TypeHandler以及覆盖默认枚举解析器的步骤。通过这些优化,提高了开发效率并增强了代码的可读性。

可能你会好奇官网有教程,看这篇文章干嘛。相信我这是一个系列,整个系列包含了开发过程应该注意的问题比如枚举、表连接、查询、缓存、开发插件等。看下去会有帮助

目标

此框架介绍是一个系列,以我开源的项目《开发者指引》后台为目标。其中包含了后端后台开发的大部分功能。

背景

作为后端开发,当然经常与数据打交道。经常性的CRUD,我们可能在一遍遍的重复增、删、改、查。这样的代码写的当然很累。而且没有技术含量。因此选择好ORM框架是非常关键的。我们经常了解到的就是Hibernate、Mybatis这两大框架。
它们有各自的优点。Hibernate拥有自己的数据操作语言与数据库解耦。但我本人非常喜欢Mybatis框架的设计方法,因此我总喜欢明确的知道最终的sql是什么样的,并且我可以轻易的修改它。它利用xml的方式动态的对sql进行解析处理,极大的增加了灵活性,使我们在写一些统计、分析、表连接等查询操作更加得心应手,但是它有一个非常麻烦的东西就是xml。常见的CRUD也需要在xml中配置,虽然mybatis有自己的生成器,我认为还不够完善。因此我们选择了mybatis-plus这个基于mybatis的第三方框架来完成我们的ORM操作。

了解mybatis

在Spring Boot下我们集成mybatis后,对数据操作核心xml、mapper,xml可以利用工具生成,所有的基础CRUD都在xml中,当我们要新增sql方法时,需要面对非常冗余的xml,是非常痛苦的。因此我选择在xml中解放出来,让mybatis-plus来完成。

了解mybatis-plus(简称mp)

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。具体如何详细的配置mp请看看官网。本篇文章主要对mp-generator工具不足之处做改进。

添加maven依赖

我最烦的就是添加依赖,因为我总是记不住。不得不去查找。现在我不用担心了,因为我们开发的就是这样后台工具,只需要在后台输入关键字,需要的maven依赖就会查询出来。类似与spring boot的快速开始,但这个功能将会更加强大。不仅如此它还包含配置文件的字段查询。以及详细的注释。

假设我们使用数据库为mysql,Spring Boot的maven假设建立好了之后添加如下依赖


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

配置mybatis-plus-generator

在开发时,我们会根据表结构生成后端的entity、mapper、service、controller这些层的代码文件。mybatis-plus-generator工具是由MP提供的生成工具,但是MP官方的生成工具不支持实体字段枚举类似为枚举,所以我fork项目后自己完善了一下功能。并且它们的代码生成理念与我不同,我专注代码的持续生成,在将来我可能会设计基于代码持续生成以及测试驱动开发这两大理念来完善程序设计。
点此进入代码仓库在分支3.0中。下面的实现都需要自己拉这个仓库代码完成。

详细的生成配置我这里就不说了。这里说说新增枚举的使用方式

步骤一、枚举字段备注

在需要使用枚举的字段中使用备注。备注格式如下。目前枚举的原始类型只支持整数

CREATE TABLE `tb` (
  `type` smallint(3) COMMENT '注释[success(0):成功, fail(1):失败]',
  `status` bigint(3) COMMENT '换行的注释
                                         [
                                           login_success(0):登录成功,
                                           login_fail(1):登录失败
                                         ]',
  `user_type` varchar(20) COMMENT '具体注释的写法是比较宽泛的,只要匹配上面正则就行
   [    success (   我是具体值  )    : 我是值的描述_我可以是中英文数字和下划线_xxx_123, fail_xx_3
    (1  ) :  失败] 后面也可以跟注释'                                       
);

格式及部分源码实现参考itfsw/mybatis-generator-plugin。但是我新增了直接在Entity对象中映射为枚举类。它的依然是int类型。

步骤二、配置生成策略

//策略中新增字段
strategy.setSuperEnumClass("xxx.xxx.config.BaseEnum");
//其中字符串是你工程中BaseEnum枚举的路径。此枚举接口定义如下,这需要你自己写在自己工程中。
public interface BaseEnum<E extends Enum<E>, T> {
    /**
     * 获取枚举的值
     * @return 枚举的值
     */
    T getValue();

    /**
     * 枚举名称,在json序列号显示
     * @return
     */
    String getName();
}

配置好之后,点击代码生成。此时实体对应的字段已经是枚举类型了,但是枚举类型mybatis还无法解析,因为mybatis有默认的枚举解析器。我们需要自定义并覆盖它。

步骤三、BaseEnumTypeHandler


import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;

public  class BaseEnumTypeHandler<E extends Enum<E> & BaseEnum<E,?>> extends BaseTypeHandler<BaseEnum<E,?>> {
    /**
     * 枚举的class
     */
    private Class<BaseEnum<E,?>> type;
    /**
     * 枚举的每个子类枚
     */
    private BaseEnum<E,?>[] enums;

    /**
     * 一定要有默认的构造函数,不然抛出 not found method 异常
     */
    public BaseEnumTypeHandler() {
    }

    /**
     * 设置配置文件设置的转换类以及枚举类内容,供其他方法更便捷高效的实现
     *
     * @param type 配置文件中设置的转换类
     */
    public BaseEnumTypeHandler(Class<BaseEnum<E,?>> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
        this.enums = type.getEnumConstants();
        if (this.enums == null) {
            throw new IllegalArgumentException(type.getSimpleName()
                    + " does not represent an enum type.");
        }
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, BaseEnum<E,?> parameter,
                                    JdbcType jdbcType) throws SQLException {
        ps.setObject(i, parameter.getValue());

    }

    @Override
    public BaseEnum<E,?> getNullableResult(ResultSet rs, String columnName)
            throws SQLException {
        String i = rs.getString(columnName);
        if (rs.wasNull()) {
            return null;
        } else {
            return locateEnumStatus(i);
        }
    }

    @Override
    public BaseEnum<E,?> getNullableResult(ResultSet rs, int columnIndex)
            throws SQLException {
        String i = rs.getString(columnIndex);
        if (rs.wasNull()) {
            return null;
        } else {
            return locateEnumStatus(i);
        }
    }

    @Override
    public BaseEnum<E,?> getNullableResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        String i = cs.getString(columnIndex);
        if (cs.wasNull()) {
            return null;
        } else {
            return locateEnumStatus(i);
        }
    }

    /**
     * 枚举类型转换,由于构造函数获取了枚举的子类 enums,让遍历更加高效快捷,
     * <p>
     * 我将取出来的值 全部转换成字符串 进行比较,
     *
     * @param value 数据库中存储的自定义value属性
     * @return value 对应的枚举类
     */
    private BaseEnum<E,?> locateEnumStatus(String value) {
        for (BaseEnum<E,?> e : enums) {
            String a = Objects.toString(e.getValue());
            if (a.equals(value)) {
                return e;
            }
        }
        throw new IllegalArgumentException("未知的枚举类型:" + value + ",请核对"
                + type.getSimpleName());
    }
}

步骤四、覆盖默认的枚举解析器


import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
@Order(1) // 通过order值的大小来决定启动的顺序
public class InitSpringBoot implements CommandLineRunner {

	@Resource
	SqlSessionFactory sqlSessionFactory;
	@Override
	public void run(String... args) throws Exception {
		sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().setDefaultEnumTypeHandler(BaseEnumTypeHandler.class);
	}
 
}

在spring boot的所以bean初始化完毕后,我们获取sqlSessionFactory来修改默认的枚举解析器。不要注入TypeHandlerRegistery因此这不是bean管理,而是直接new的。
同时也不要用mybatis.type-handlers-package=xxx来配置,这将是无效。只有手动覆盖默认枚举解析器才行。至于为什么,应该是TypeHandlerRegistery中解析器的初始化方式决定的。个人感觉TypeHandlerRegistery没有管理typeHandlerMap的入口,对于类型扩展很不方便。因为mybatis默认注入进去的,我们没办法修改。

总结

这章内容就到这里。今后我们将介绍测试框架、json枚举序列化等内容。
下面是放github上的配置代码
mybatis-plus本教程的配置代码
枚举类、TypeHandler、装载TypeHandler

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值