Learning Spring Boot: (12) Custom Enum Converter in Mybatis

foreword

In the use of Mybatis in Spring Boot, I encountered that the field is an enumeration type, and the database stores the value of the enumeration, and found that it cannot be automatically loaded.

solve

Built-in enum converter

MyBatis has two built-in enumeration converters: org.apache.ibatis.type.EnumTypeHandlerand org.apache.ibatis.type.EnumOrdinalTypeHandler.

EnumTypeHandler

The default enum converter in mybatis is to get the nameproperties .

EnumOrdinalTypeHandler

Getting the ordinalproperties is like an index, but incremented from 1.

Therefore, the two converters provided above cannot meet our needs, and we need to customize a converter.

custom enum converter

MyBatis provides org.apache.ibatis.type.BaseTypeHandlerclasses for our own extension type converters, and the above EnumTypeHandler和EnumOrdinalTypeHandleralso implement this interface.

Inheritance needs to implement a BaseTypeHandler<T>total of 4 methods:
1. void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
When defining the setting parameters, how to convert the parameters of the Java type to the corresponding database types;
2. T getNullableResult(ResultSet rs, String columnName)
When defining the field data obtained by the field name, how to convert the database type to The corresponding Java type;
3. T getNullableResult(ResultSet rs, int columnIndex)
Used to define how to convert the database type to the corresponding Java type when the field data is obtained through the field index;
4. T getNullableResult(CallableStatement cs, int columnIndex)
How to convert the database type to the corresponding Java type after calling the stored procedure with the definition.

Defining an enum generic behavior

Defines the general behavior of an enumeration, and standardizes the implementation of the enumeration.

public interface BaseEnum<E extends Enum<?>, T> {
    /**
     * 获取枚举的值
     * @return 枚举的值
     */
    T getValue();
}

Define the enumeration you need:

public class SysConstant {

    /**
     * 人员状态
     */
    public enum SysUserStatus implements BaseEnum<SysUserStatus, String> {
        /**
         * 账户已经激活(默认)
         */
        ACTIVE("1"),
        /**
         * 账户锁定
         */
        LOCK("0");

        private String value;

        private SysUserStatus(String value) {
            this.value = value;
        }

        @Override
        public String getValue() {
            return value;
        }
    }

    /**
     * 人员类型
     */
    public enum SysUserType implements BaseEnum<SysUserType, String> {
        /**
         * 普通用户
         */
        USER("1"),
        /**
         * 系统管理员
         */
        ADMIN("0");

        private String value;

        private SysUserType(String value) {
            this.value = value;
        }

        @Override
        public String getValue() {
            return value;
        }
    }
}
Implement custom converters

Customize a basic enum converter tool, if you have other needs, you can customize this base class.

package com.wuwii.common.util;

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;

/**
 * 参考 http://blog.csdn.net/fighterandknight/article/details/51520595 
 * 进行对本地项目的优化
 * <p>
 * 解决 Mybatis 中枚举的问题,
 * 获取 ResultSet 的值都是获取字符串的,然后比较字符串,以便通用。
 *
 * @author Zhang Kai
 * @version 1.0
 * @since <pre>2018/2/9 17:26</pre>
 */
public abstract class BaseEnumTypeHandler<E extends Enum<E> & BaseEnum> extends BaseTypeHandler<E> {
    /**
     * 枚举的class
     */
    private Class<E> type;
    /**
     * 枚举的每个子类枚
     */
    private E[] enums;

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

    /**
     * 设置配置文件设置的转换类以及枚举类内容,供其他方法更便捷高效的实现
     *
     * @param type 配置文件中设置的转换类
     */
    public BaseEnumTypeHandler(Class<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, E parameter,
                                    JdbcType jdbcType) throws SQLException {
        /*
         * BaseTypeHandler已经帮我们做了parameter的null判断
         * 数据库存储的是枚举的值,所以我们这里使用 value , 如果需要存储 name,可以自定义修改
         */
        if (jdbcType == null) {
            ps.setString(i, Objects.toString(parameter.getValue()));
        } else {
            ps.setObject(i, parameter.getValue(), jdbcType.TYPE_CODE);
        }
    }

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

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

    @Override
    public 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 E locateEnumStatus(String value) {
        for (E e : enums) {
            if (Objects.toString(e.getValue()).equals(value)) {
                return e;
            }
        }
        throw new IllegalArgumentException("未知的枚举类型:" + value + ",请核对"
                + type.getSimpleName());
    }
}
configuration converter

After writing the enum converter, we need to define which enums it converts.
Can be configured in the Mybatis configuration file

<typeHandlers>
    <typeHandler handler="com.example.typeHandler.CodeEnumTypeHandler" javaType="com.example.entity.enums.ComputerState"/>
</typeHandlers>
optimization

Adding typeHandler to MyBatis is used to specify which classes use our custom converter. Once there are more enumeration classes in the system, the configuration file of MyBatis will become very troublesome and error-prone to maintain .

Method 1 :
Define a EnumTypeHandlerto inherit ours BaseEnumTypeHandler. Then use annotation @MappedTypestype conversion.

package com.wuwii.module.sys.common.handle;

import com.wuwii.common.handle.BaseEnumTypeHandler;
import com.wuwii.common.util.BaseEnum;
import com.wuwii.module.sys.common.util.SysConstant;
import org.apache.ibatis.type.MappedTypes;

/**
 * 枚举转换的公共模块
 *
 * @author Zhang Kai
 * @version 1.0
 * @since <pre>2018/2/9 18:12</pre>
 */
@MappedTypes(value = {SysConstant.SysUserStatus.class, SysConstant.SysUserType.class})
public class SysEnumTypeHandler<E extends Enum<E> & BaseEnum> extends BaseEnumTypeHandler<E> {
    /**
     * 设置配置文件设置的转换类以及枚举类内容,供其他方法更便捷高效的实现
     *
     * @param type 配置文件中设置的转换类
     */
    public SysEnumTypeHandler(Class<E> type) {
        super(type);
    }
}

It needs to be configured in the system configuration file

# 多个模块的的多个 包配置可以使用逗号分开。
mybatis:
    typeHandlersPackage: com.wuwii.module.sys.common.handle,com.wuwii.module.task.common.handle

Method 2 :
If you customize it in your project SqlSessionFactory, it is recommended to use the following method, and you do not need to configure it multiple times for one use.

How to use enumeration gracefully in MyBatis

In = "Scenario 6. Optimization

Method 3 :
There is also a personal modification of the source code to achieve, if you are interested, you can take a look at:

Modify MyBatis source code to implement scan registration enumeration - specific implementation

Reference article

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325632568&siteId=291194637