解决MyBatis逆向工程_数据库字段的int型变成了Integer型_实现int型转成int型_超级详细

一. 问题背景

IDE: IntelliJ IDEA 2019.1版本
后台: SpringBoot 2.3.1版本
MySQL: 5.7.25版本

情况:使用mybatis逆向工程生成实体类,但是数据库里面表的字段本来是int型,到了实体类就变成了Integer型,我的需求是int型->int型,而不是int型->Integer型。因此需要修改配置。本文将介绍修改字段类型映射的方法以及原理,与字段类型映射有关的问题都大概会讲到。

效果如下:
在这里插入图片描述
如果上图不是大家想要的效果或者不想了解字段类型映射的原理,可以忽略下面的内容了,因为篇幅比较长。 如果对mybatis逆向工程有兴趣或想知道怎么搭建逆向工程,可以看看这篇MyBatis generator逆向工程_超详细解说_0基础入门_一篇就够了

本文适合将数据库字段的short型转为Integer型、tinyint转化为Byte类型、tinyint转为Integer类型等等以及类型转换原理的情况。

二. 参考博客

在这里感谢以下博客:

  1. 修改mybatis-generator中数据库类型和Java类型的映射关系适合修改简单的数据库类型和Java类型的映射关系。操作简单
  2. 自定义mybatis-generator自动生成代码时的类型转换适合一般情况的数据库类型转为Java类型。操作简单。
  3. mybatis3逆向工程Short类型转Integer适合解决不了类型转换的情况,当然本文也有讲述,而且非常详细。

三. 数据库表的int型转为实体类的int型

3.1 前言

本文需要搭好mybatis逆向工程环境,详情可以查看MyBatis generator逆向工程_超详细解说_0基础入门_一篇就够了中的搭建环境部分。这里第三节给出的实现步骤是解决int型->int型。其他类型转其他类型后面小节也会讲述。

3.2 步骤

创建一个类(类名随意),并继承JavaTypeResolverDefaultImpl。代码如下:

/**
 * 解决mybatis逆向工程数据表字段映射到实体类属性类型 不符合需求 的问题
 * 即数据表字段的int型映射到Java实体类的属性变成了Integer
 **/
public class MyJavaTypeResolverDefaultImpl extends JavaTypeResolverDefaultImpl {
    @Override
    protected FullyQualifiedJavaType overrideDefaultType(IntrospectedColumn column, FullyQualifiedJavaType defaultType) {
        FullyQualifiedJavaType answer = defaultType;
        switch(column.getJdbcType()) {
            case 4://int型字段的getJdbcType()都是4
                if("INT".equals(column.getActualTypeName())){
                    answer = new FullyQualifiedJavaType("int");//返回int型
                }
                break;
            case -7:
                answer = calculateBitReplacement(column, defaultType);
                break;
            case 2:
            case 3:
                answer = this.calculateBigDecimalReplacement(column, defaultType);
                break;
            case 91:
                answer = this.calculateDateType(column, defaultType);
                break;
            case 92:
                answer = this.calculateTimeType(column, defaultType);
                break;
            case 93:
                answer = this.calculateTimestampType(column, defaultType);
        }

        return answer;
    }
}

解释: 这个方法是重写父类JavaTypeResolverDefaultImpl的方法。原理后面会讲述

在generatorConfig.xml的<context>标签中,使用<javaTypeResolver>type属性配置刚才定义的类,如下:

 <!--配置生成环境-->
<context>
  <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal -->
   <!-- 将数据库的int映射到实体类时为int,默认是映射成Integer,因此自定义类去修改它-->
   <javaTypeResolver type="com.icbc.demo.config.MyJavaTypeResolverDefaultImpl">
       <property name="forceBigDecimals" value="false"/>
   </javaTypeResolver>
</context>

解释:type的值必须是全类名(要有全路径)

至此,启动逆向工程即可。注意需要用类启动方式,不能用插件启动,插件启动会有很多问题

四. 其他类型转换的方案

一般情况下,只需配置简单的配置项或者修改一两句代码就可以了,无需使用上面那种自定义类的方法。

4.1 方案一

在generatorConfig.xml中的<table>标签里,使用<columnOverride >,如下:

<table tableName="user2"  domainObjectName="User2"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
  <columnOverride column="数据库字段类型" javaType="你想要的类型"/>
</table>

4.2 方案二

创建一个类实现JavaTypeResolver接口,将JavaTypeResolverDefaultImpl里面的所有代码都复制到自定义类里面。

打开JavaTypeResolverDefaultImpl源码,它有一个构造方法,如下:

public JavaTypeResolverDefaultImpl() {
        this.typeMap.put(2003, new JavaTypeResolverDefaultImpl.JdbcTypeInformation("ARRAY", new FullyQualifiedJavaType(Object.class.getName())));
        this.typeMap.put(-5, new JavaTypeResolverDefaultImpl.JdbcTypeInformation("BIGINT", new FullyQualifiedJavaType(Long.class.getName())));
        ...
    }

我们只需找到数据库字段的类型,并 将你想要的类型修改到后面(当然这是在自定义的那个类里面修改,因为没有下载源码的话,源码是只读的,不可写) 即可,如下:
在这里插入图片描述
然后启动逆向工程即可。推荐使用类启动方式,插件启动有很多问题。

五. Mybatis逆向工程类型转换的原理

首先要知道mybatis使用JavaTypeResolverDefaultImpl实现类型转换的,这个类实现了JavaTypeResolver接口。(从这里可以知道,我们想自定义类型转换,只需创建一个类并实现这个接口,根据JavaTypeResolverDefaultImpl中的代码复制过去就可以,然后修改成自己想要的效果)

JavaTypeResolverDefaultImpl其中最最最关键的方法是calculateJavaType(),如下:

public FullyQualifiedJavaType calculateJavaType(IntrospectedColumn introspectedColumn) {
        FullyQualifiedJavaType answer = null;//1.这是要返回出去的对象
        JavaTypeResolverDefaultImpl.JdbcTypeInformation jdbcTypeInformation = 
                       (JavaTypeResolverDefaultImpl.JdbcTypeInformation)
                       //3.getJdbcType()获取数据库字段类型,然后从typeMap中拿到要转换成什么类型
                        this.typeMap.get(introspectedColumn.getJdbcType());
        if (jdbcTypeInformation != null) {
            //4.拿到类型的全称,比如java.lang.String、Java.lang.Integer
            answer = jdbcTypeInformation.getFullyQualifiedJavaType();
            //5.重写默认的类型。有些特殊的数据库类型需要做特殊处理才能转成Java类型。这里很关键
            answer = this.overrideDefaultType(introspectedColumn, answer);
        }

        return answer;//2.返回出去的类型是FullyQualifiedJavaType ,必须知道这一点
    }

再看看overrideDefaultType(),如下:

protected FullyQualifiedJavaType overrideDefaultType(IntrospectedColumn column, FullyQualifiedJavaType defaultType) {
        FullyQualifiedJavaType answer = defaultType;
        switch(column.getJdbcType()) {//1.先获取数据库字段类型
        case -7: //2.做case匹配
            //3.处理Bit类型,看看是否还需要将他转成什么类型
            answer = this.calculateBitReplacement(column, defaultType);
            break;
        case 2:
        case 3:
            //4.处理BigDecimal类型,看看是否还需要将他转成什么类型
            answer = this.calculateBigDecimalReplacement(column, defaultType);
            break;
        case 91:
            //5.处理Date类型,看看是否还需要将他转成什么类型
            answer = this.calculateDateType(column, defaultType);
            break;
        case 92:
             //6.处理Time类型,看看是否还需要将他转成什么类型
            answer = this.calculateTimeType(column, defaultType);
            break;
        case 93:
            //7.处理Timestamp类型,看看是否还需要将他转成什么类型
            answer = this.calculateTimestampType(column, defaultType);
        }

        return answer;//8.处理完后返回出去
    }

解释: 先获取数据库字段的类型,做case匹配,再进行相应的处理。处理完后返回出去。

总结1:首先是calculateJavaType()方法,其中会获取数据库字段的类型,然后根据mybatis写好的转换类型关系获取要转成什么类型。如果获取到的对象不为空,最终会调用overrideDefaultType()方法,该方法返回出去的类型就是最终要转换的类型。

总结2:要转换成什么类型,最终由overrideDefaultType()方法决定,因此我们需要修改类型转换关系,只需修改overrideDefaultType()方法。

上面 有很多calculateXxxxType()方法,因此 我们来看一两个这些方法,如下:

protected FullyQualifiedJavaType calculateTimestampType(IntrospectedColumn column, FullyQualifiedJavaType defaultType) {
        FullyQualifiedJavaType answer;
        if (this.useJSR310Types) {
            answer = new FullyQualifiedJavaType("java.time.LocalDateTime");
        } else {
            answer = defaultType;
        }

        return answer;
    }

    protected FullyQualifiedJavaType calculateBitReplacement(IntrospectedColumn column, FullyQualifiedJavaType defaultType) {
        FullyQualifiedJavaType answer;
        if (column.getLength() > 1) {
            answer = new FullyQualifiedJavaType("byte[]");
        } else {
            answer = defaultType;
        }

        return answer;
    }

可以看到,这些calculateXxxxType()方法,最终都是返回new FullyQualifiedJavaType("类型")

总结:因此我们可以使用new FullyQualifiedJavaType("类型")返回出去我们想要转换成的类型。

再来看看可以填写什么类型,点进看看FullyQualifiedJavaType类,发现有好多这些get()方法,并且返回值都是new FullyQualifiedJavaType("类型"),如下:

public static final FullyQualifiedJavaType getIntInstance() {
        if (intInstance == null) {
            intInstance = new FullyQualifiedJavaType("int");
        }

        return intInstance;
    }

    public static final FullyQualifiedJavaType getNewMapInstance() {
        return new FullyQualifiedJavaType("java.util.Map");
    }

    public static final FullyQualifiedJavaType getNewListInstance() {
        return new FullyQualifiedJavaType("java.util.List");
    }

    public static final FullyQualifiedJavaType getNewHashMapInstance() {
        return new FullyQualifiedJavaType("java.util.HashMap");
    }

    public static final FullyQualifiedJavaType getNewArrayListInstance() {
        return new FullyQualifiedJavaType("java.util.ArrayList");
    }

    public static final FullyQualifiedJavaType getNewIteratorInstance() {
        return new FullyQualifiedJavaType("java.util.Iterator");
    }

    public static final FullyQualifiedJavaType getStringInstance() {
        if (stringInstance == null) {
            stringInstance = new FullyQualifiedJavaType("java.lang.String");
        }

        return stringInstance;
    }

    public static final FullyQualifiedJavaType getBooleanPrimitiveInstance() {
        if (booleanPrimitiveInstance == null) {
            booleanPrimitiveInstance = new FullyQualifiedJavaType("boolean");
        }

        return booleanPrimitiveInstance;
    }

    public static final FullyQualifiedJavaType getObjectInstance() {
        if (objectInstance == null) {
            objectInstance = new FullyQualifiedJavaType("java.lang.Object");
        }

        return objectInstance;
    }

    public static final FullyQualifiedJavaType getDateInstance() {
        if (dateInstance == null) {
            dateInstance = new FullyQualifiedJavaType("java.util.Date");
        }

        return dateInstance;
    }

如以上代码所示,都是可以填写到new FullyQualifiedJavaType("类型")中的类型。

总结:我们最最最最终可以使用new FullyQualifiedJavaType("类型")将自己想要的类型返回出去。

六. int型转成int型的实现原理

第三节给出了int型->int型的解决方法,它的原理是这样的。

来到这个calculateJavaType()方法(可以在这个方法打断点)如下:

 public FullyQualifiedJavaType calculateJavaType(IntrospectedColumn introspectedColumn) {
        FullyQualifiedJavaType answer = null;
        JavaTypeResolverDefaultImpl.JdbcTypeInformation jdbcTypeInformation 
                     = (JavaTypeResolverDefaultImpl.JdbcTypeInformation)
                       /**1.因为数据库字段是int型,因此getJdbcType()拿到的是4,
                        *typeMap.get(4)拿到的是Integer
                        */
                        this.typeMap.get(introspectedColumn.getJdbcType());
        if (jdbcTypeInformation != null) {
            //2.拿到全称类型java.lang.Integer
            answer = jdbcTypeInformation.getFullyQualifiedJavaType();
            /**3.传入参数:introspectedColumn存储数据库字段的信息;
             *    answer存储要转换成什么类型
             */
            answer = this.overrideDefaultType(introspectedColumn, answer);
        }

        return answer;
    }

来到overrideDefaultType()这个方法,如下:

protected FullyQualifiedJavaType overrideDefaultType(IntrospectedColumn column, FullyQualifiedJavaType defaultType) {
        FullyQualifiedJavaType answer = defaultType;
        /*1.getJdbcType()拿到的是4,但是下面没有case 4,从而不能做进一步处理。
         * 因此最终返回出去的是没有改变过的answer对象,也就是java.lang.Integer类型
         */
        switch(column.getJdbcType()) {
        case -7:
            answer = this.calculateBitReplacement(column, defaultType);
            break;
        case 2:
        case 3:
            answer = this.calculateBigDecimalReplacement(column, defaultType);
            break;
        case 91:
            answer = this.calculateDateType(column, defaultType);
            break;
        case 92:
            answer = this.calculateTimeType(column, defaultType);
            break;
        case 93:
            answer = this.calculateTimestampType(column, defaultType);
        }

        return answer;
    }

因此我写了一个类并继承MyJavaTypeResolverDefaultImpl,重写了这个overrideDefaultType()方法,如下:

/**
 * 解决mybatis逆向工程数据表字段映射到实体类属性类型 不符合需求 的问题
 * 即数据表字段的int型映射到Java实体类的属性变成了Integer
 **/
public class MyJavaTypeResolverDefaultImpl extends JavaTypeResolverDefaultImpl {
    @Override
    protected FullyQualifiedJavaType overrideDefaultType(IntrospectedColumn column, FullyQualifiedJavaType defaultType) {
        FullyQualifiedJavaType answer = defaultType;
        switch(column.getJdbcType()) {
            case 4: //int型字段的getJdbcType()都是4
                if("INT".equals(column.getActualTypeName())){
                    answer = new FullyQualifiedJavaType("int");//返回int型
                }
                break;
            case -7:
                answer = calculateBitReplacement(column, defaultType);
                break;
            case 2:
            case 3:
                answer = this.calculateBigDecimalReplacement(column, defaultType);
                break;
            case 91:
                answer = this.calculateDateType(column, defaultType);
                break;
            case 92:
                answer = this.calculateTimeType(column, defaultType);
                break;
            case 93:
                answer = this.calculateTimestampType(column, defaultType);
        }

        return answer;
    }


}

从上面可以看到,其实就是利用了最最最终会调用到的overrideDefaultType(),以及最终返回出去的new FullyQualifiedJavaType("类型")

有问题的小伙伴可留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值