文章目录
一. 问题背景
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类型等等以及类型转换原理的情况。
二. 参考博客
在这里感谢以下博客:
- 修改mybatis-generator中数据库类型和Java类型的映射关系。适合修改简单的数据库类型和Java类型的映射关系。操作简单
- 自定义mybatis-generator自动生成代码时的类型转换。适合一般情况的数据库类型转为Java类型。操作简单。
- 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("类型")
。
有问题的小伙伴可留言