- 需求
mybatis目前已经内嵌入了springboot中了,这说明其目前在数据访问层的绝对优势。而我们在开发的过程中,往往会在程序中使用枚举(enum) 来表示一些状态或选项,而在数据库中使用数字来存储。这样做的好处是在程序中使用enum更直观的可以知道每个值代表的状态及含义,还可以做国际化的功能。那么这样会带来一个问题那就是:程序中的枚举 与 数据库中的数字 转换问题。 - 介绍
抱歉,最近因为实在太忙,所以写一半就停了。等有空继续。不将就哈。停了大概一周的时间,在周一的早上继续来完成这篇文章。
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。
对于enum而言,在mybatis中已经存在了EnumTypeHandler和EnumOrdinalTypeHandler两大处理器,他们都是继承自BaseTypeHandler处理器,那为何不能使用mybatis自定义的enum处理器而是要自己再定义枚举处理器呢?下面为您一步一步剖析。 - 分析
首先说说EnumTypeHandler与EnumOrdinalTypeHandler两者之间的区别吧:
EnumTypeHandler存入数据库的是枚举的name,EnumOrdinalTypeHandler存入数据库的是枚举的位置。例如下方的枚举,当我们有一个枚举值是EStatus.init时,这时我们使用mybatis EnumTypeHandler存入数据库的是"init"字符串;而EnumOrdinalTypeHandler存入的是3,因为init是第四个值,第一个值disable的index是0。- public enum EStatus {
- disable("0"), enable("1"), deleted("2"),
- init("10"), start("11"), wait("12"), end("13");
- }
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by Fernflower decompiler)
- //
- package org.apache.ibatis.type;
- import java.sql.CallableStatement;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- public class EnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
- private Class<E> type;
- public EnumTypeHandler(Class<E> type) {
- if(type == null) {
- throw new IllegalArgumentException("Type argument cannot be null");
- } else {
- this.type = type;
- }
- }
- public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
- if(jdbcType == null) {
- ps.setString(i, parameter.name());
- } else {
- ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE);
- }
- }
- public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
- String s = rs.getString(columnName);
- return s == null?null:Enum.valueOf(this.type, s);
- }
- public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
- String s = rs.getString(columnIndex);
- return s == null?null:Enum.valueOf(this.type, s);
- }
- public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
- String s = cs.getString(columnIndex);
- return s == null?null:Enum.valueOf(this.type, s);
- }
- }
而在将数据库字段转java Bean字段的时候使用的方法getNullableResult中都是调用Enum.valueOf(this.type, s),而这个方法底层调用的是get(name)方法。
所以EnumTypeHandler对我们来说并不适用,因为它在java Bean转数据库数据时获取的是枚举的name而不是value。
而EnumOrdinalTypeHandler存入数据库的是参数的位置,所以这两个处理器都不是我们想要的。但是我们可以根据EnumTypeHandler的设计思想转变一下,变成符合我们想要的统一处理器。 - 教程
首先我们定义一个BaseEnumTypeHandler继承BaseTypeHandler,并实现抽象方法。
- package com.cmc.base.handler;
- import com.cmc.base.enums.EStatus;
- import com.cmc.base.utils.EnumUtil;
- import org.apache.ibatis.type.BaseTypeHandler;
- import org.apache.ibatis.type.JdbcType;
- import org.apache.ibatis.type.MappedTypes;
- import java.sql.CallableStatement;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- public class BaseEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
- private Class<E> type;
- public BaseEnumTypeHandler() {}
- public BaseEnumTypeHandler(Class<E> type) {
- this.type = type;
- }
- @Override
- public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
- if (jdbcType == null) {
- ps.setString(i, parameter.toString());
- } else {
- ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE);
- }
- }
- @Override
- public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
- return get(rs.getString(columnName));
- }
- @Override
- public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
- return get(rs.getString(columnIndex));
- }
- @Override
- public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
- return get(cs.getString(columnIndex));
- }
- private <E extends Enum<E>> E get(String v) {
- if (v == null) return null;
- if (StringUtils.isNumeric(v)) {
- return get(type, Integer.parseInt(v));
- } else {
- return Enum.valueOf(type, v);
- }
- }
- private <E extends Enum<E>> E get(Class<E> type, int v) {
- Method method = null;
- E result = null;
- try {
- method = type.getMethod("get", int.class);
- result = (E)method.invoke(type, v);
- } catch (NoSuchMethodException e) {
- result = Enum.valueOf(type, String.valueOf(v));
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- return result;
- }
- }
所以我们需要在枚举中定义toString和get方法
- package com.cmc.base.enums;
- /**
- * 状态枚举
- * @author chenmc
- *
- */
- public enum EStatus {
- disable("0"), enable("1"), deleted("2"),
- init("10"), start("11"), wait("12"), end("13");
- private final String value;
- private EStatus(String v) {
- this.value = v;
- }
- public String toString() {
- return this.value;
- }
- public static EStatus get(int v) {
- String str = String.valueOf(v);
- return get(str);
- }
- public static EStatus get(String str) {
- for (EStatus e : values()) {
- if(e.toString().equals(str)) {
- return e;
- }
- }
- return null;
- }
- /*public static String getName(EStatus e, Locale locale) {
- return I18N.getEnumName(e, locale);
- }*/
- }
既然方法都写好了,那下面给大家讲讲如何在springboot中使用。
1.在application.properties或者application.yml中添加
- mybatis:
- mapperLocations: classpath:mapper/*.xml
- typeAliasesPackage: com.cmc.schedule.model.entity
- typeHandlersPackage: com.cmc.schedule.model.handler
- package com.cmc.schedule.model.handler;
- import com.cmc.base.enums.EStatus;
- import com.cmc.base.handler.BaseEnumTypeHandler;
- import com.cmc.schedule.model.enums.EFeedback;
- import org.apache.ibatis.type.MappedTypes;
- /**
- * @author chenmc
- */
- @MappedTypes(value = { EFeedback.class, EStatus.class})
- public class EnumTypeHandler<E extends Enum<E>> extends BaseEnumTypeHandler<E> {
- public EnumTypeHandler(Class<E> type) {
- super(type);
- }
- }
SpringBoot Mybatis EnumTypeHandler自定义统一处理器
最新推荐文章于 2024-10-09 17:21:03 发布