diff --git a/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/continew/starter/data/mybatis/plus/base/IBaseEnum.java b/continew-starter-core/src/main/java/top/continew/starter/core/enums/BaseEnum.java similarity index 84% rename from continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/continew/starter/data/mybatis/plus/base/IBaseEnum.java rename to continew-starter-core/src/main/java/top/continew/starter/core/enums/BaseEnum.java index 3e623892..5c5893d8 100644 --- a/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/continew/starter/data/mybatis/plus/base/IBaseEnum.java +++ b/continew-starter-core/src/main/java/top/continew/starter/core/enums/BaseEnum.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package top.continew.starter.data.mybatis.plus.base; - -import com.baomidou.mybatisplus.annotation.IEnum; +package top.continew.starter.core.enums; import java.io.Serializable; @@ -27,7 +25,14 @@ * @author Charles7c * @since 1.0.0 */ -public interface IBaseEnum extends IEnum { +public interface BaseEnum { + + /** + * 枚举值 + * + * @return 枚举值 + */ + T getValue(); /** * 枚举描述 diff --git a/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/continew/starter/data/mybatis/plus/autoconfigure/MybatisPlusAutoConfiguration.java b/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/continew/starter/data/mybatis/plus/autoconfigure/MybatisPlusAutoConfiguration.java index 2bb8a9b8..a7c48541 100644 --- a/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/continew/starter/data/mybatis/plus/autoconfigure/MybatisPlusAutoConfiguration.java +++ b/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/continew/starter/data/mybatis/plus/autoconfigure/MybatisPlusAutoConfiguration.java @@ -17,6 +17,7 @@ package top.continew.starter.data.mybatis.plus.autoconfigure; import cn.hutool.extra.spring.SpringUtil; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusPropertiesCustomizer; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler; import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; @@ -40,6 +41,7 @@ import top.continew.starter.data.mybatis.plus.autoconfigure.idgenerator.MyBatisPlusIdGeneratorConfiguration; import top.continew.starter.data.mybatis.plus.datapermission.DataPermissionFilter; import top.continew.starter.data.mybatis.plus.datapermission.DataPermissionHandlerImpl; +import top.continew.starter.data.mybatis.plus.handler.MybatisBaseEnumTypeHandler; /** * MyBatis Plus 自动配置 @@ -57,6 +59,14 @@ public class MybatisPlusAutoConfiguration { private static final Logger log = LoggerFactory.getLogger(MybatisPlusAutoConfiguration.class); + /** + * MyBatis Plus 配置 + */ + @Bean + public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() { + return properties -> properties.getConfiguration().setDefaultEnumTypeHandler(MybatisBaseEnumTypeHandler.class); + } + /** * MyBatis Plus 插件配置 */ diff --git a/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/continew/starter/data/mybatis/plus/handler/MybatisBaseEnumTypeHandler.java b/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/continew/starter/data/mybatis/plus/handler/MybatisBaseEnumTypeHandler.java new file mode 100644 index 00000000..ae587cdb --- /dev/null +++ b/continew-starter-data/continew-starter-data-mybatis-plus/src/main/java/top/continew/starter/data/mybatis/plus/handler/MybatisBaseEnumTypeHandler.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.data.mybatis.plus.handler; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.baomidou.mybatisplus.annotation.IEnum; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils; +import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import org.apache.ibatis.reflection.DefaultReflectorFactory; +import org.apache.ibatis.reflection.MetaClass; +import org.apache.ibatis.reflection.ReflectorFactory; +import org.apache.ibatis.reflection.invoker.Invoker; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; +import top.continew.starter.core.enums.BaseEnum; + +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 自定义枚举属性转换器 + * + * @author hubin + * @author Charles7c + * @since 2.3.1 + */ +public class MybatisBaseEnumTypeHandler> extends BaseTypeHandler { + + private static final Map TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap<>(); + private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory(); + private final Class enumClassType; + private final Class propertyType; + private final Invoker getInvoker; + + public MybatisBaseEnumTypeHandler(Class enumClassType) { + if (enumClassType == null) { + throw new IllegalArgumentException("Type argument cannot be null"); + } + this.enumClassType = enumClassType; + MetaClass metaClass = MetaClass.forClass(enumClassType, REFLECTOR_FACTORY); + String name = "value"; + if (!BaseEnum.class.isAssignableFrom(enumClassType) && !IEnum.class.isAssignableFrom(enumClassType)) { + name = findEnumValueFieldName(this.enumClassType).orElseThrow(() -> new IllegalArgumentException(String + .format("Could not find @EnumValue in Class: %s.", this.enumClassType.getName()))); + } + this.propertyType = ReflectionKit.resolvePrimitiveIfNecessary(metaClass.getGetterType(name)); + this.getInvoker = metaClass.getGetInvoker(name); + } + + /** + * 查找标记标记EnumValue字段 + * + * @param clazz class + * @return EnumValue字段 + */ + public static Optional findEnumValueFieldName(Class clazz) { + if (clazz != null && clazz.isEnum()) { + String className = clazz.getName(); + return Optional.ofNullable(CollectionUtils.computeIfAbsent(TABLE_METHOD_OF_ENUM_TYPES, className, key -> { + Optional fieldOptional = findEnumValueAnnotationField(clazz); + return fieldOptional.map(Field::getName).orElse(null); + })); + } + return Optional.empty(); + } + + private static Optional findEnumValueAnnotationField(Class clazz) { + return Arrays.stream(clazz.getDeclaredFields()) + .filter(field -> field.isAnnotationPresent(EnumValue.class)) + .findFirst(); + } + + /** + * 判断是否为MP枚举处理 + * + * @param clazz class + * @return 是否为MP枚举处理 + */ + public static boolean isMpEnums(Class clazz) { + return clazz != null && clazz.isEnum() && (BaseEnum.class.isAssignableFrom(clazz) || IEnum.class + .isAssignableFrom(clazz) || findEnumValueFieldName(clazz).isPresent()); + } + + @SuppressWarnings("Duplicates") + @Override + public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException { + if (jdbcType == null) { + ps.setObject(i, this.getValue(parameter)); + } else { + // see r3589 + ps.setObject(i, this.getValue(parameter), jdbcType.TYPE_CODE); + } + } + + @Override + public E getNullableResult(ResultSet rs, String columnName) throws SQLException { + Object value = rs.getObject(columnName, this.propertyType); + if (null == value || rs.wasNull()) { + return null; + } + return this.valueOf(value); + } + + @Override + public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + Object value = rs.getObject(columnIndex, this.propertyType); + if (null == value || rs.wasNull()) { + return null; + } + return this.valueOf(value); + } + + @Override + public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + Object value = cs.getObject(columnIndex, this.propertyType); + if (null == value || cs.wasNull()) { + return null; + } + return this.valueOf(value); + } + + private E valueOf(Object value) { + E[] es = this.enumClassType.getEnumConstants(); + return Arrays.stream(es).filter(e -> equalsValue(value, getValue(e))).findAny().orElse(null); + } + + /** + * 值比较 + * + * @param sourceValue 数据库字段值 + * @param targetValue 当前枚举属性值 + * @return 是否匹配 + */ + private boolean equalsValue(Object sourceValue, Object targetValue) { + String sValue = StringUtils.toStringTrim(sourceValue); + String tValue = StringUtils.toStringTrim(targetValue); + if (sourceValue instanceof Number && targetValue instanceof Number && new BigDecimal(sValue) + .compareTo(new BigDecimal(tValue)) == 0) { + return true; + } + return Objects.equals(sValue, tValue); + } + + private Object getValue(Object object) { + try { + return this.getInvoker.invoke(object, new Object[0]); + } catch (ReflectiveOperationException e) { + throw ExceptionUtils.mpe(e); + } + } +} diff --git a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/converter/ExcelBaseEnumConverter.java b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/converter/ExcelBaseEnumConverter.java index eb6be790..c32c0b26 100644 --- a/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/converter/ExcelBaseEnumConverter.java +++ b/continew-starter-extension/continew-starter-extension-crud/continew-starter-extension-crud-mp/src/main/java/top/continew/starter/extension/crud/converter/ExcelBaseEnumConverter.java @@ -25,20 +25,20 @@ import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.property.ExcelContentProperty; import top.continew.starter.core.constant.StringConstants; -import top.continew.starter.data.mybatis.plus.base.IBaseEnum; +import top.continew.starter.core.enums.BaseEnum; /** * Easy Excel 枚举接口转换器 * - * @see IBaseEnum + * @see BaseEnum * @author Charles7c * @since 1.2.0 */ -public class ExcelBaseEnumConverter implements Converter> { +public class ExcelBaseEnumConverter implements Converter> { @Override - public Class supportJavaTypeKey() { - return IBaseEnum.class; + public Class supportJavaTypeKey() { + return BaseEnum.class; } @Override @@ -50,17 +50,17 @@ public CellDataTypeEnum supportExcelTypeKey() { * 转换为 Java 数据(读取 Excel) */ @Override - public IBaseEnum convertToJavaData(ReadCellData cellData, - ExcelContentProperty contentProperty, - GlobalConfiguration globalConfiguration) { - return this.getEnum(IBaseEnum.class, Convert.toStr(cellData.getData())); + public BaseEnum convertToJavaData(ReadCellData cellData, + ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return this.getEnum(BaseEnum.class, Convert.toStr(cellData.getData())); } /** * 转换为 Excel 数据(写入 Excel) */ @Override - public WriteCellData convertToExcelData(IBaseEnum value, + public WriteCellData convertToExcelData(BaseEnum value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (null == value) { @@ -76,11 +76,11 @@ public WriteCellData convertToExcelData(IBaseEnum value, * @param description 描述 * @return 对应枚举 ,获取不到时为 {@code null} */ - private IBaseEnum getEnum(Class enumType, String description) { + private BaseEnum getEnum(Class enumType, String description) { Object[] enumConstants = enumType.getEnumConstants(); for (Object enumConstant : enumConstants) { - if (ClassUtil.isAssignable(IBaseEnum.class, enumType)) { - IBaseEnum baseEnum = (IBaseEnum)enumConstant; + if (ClassUtil.isAssignable(BaseEnum.class, enumType)) { + BaseEnum baseEnum = (BaseEnum)enumConstant; if (baseEnum.getDescription().equals(description)) { return baseEnum; }