feature: 为 entity 添加枚举属性的支持 close #I6W15K

This commit is contained in:
开源海哥 2023-04-16 13:23:47 +08:00
parent 254b58678c
commit 745c89683e
6 changed files with 226 additions and 13 deletions

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 com.mybatisflex.annotation;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface EnumValue {
}

View File

@ -0,0 +1,63 @@
/**
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 com.mybatisflex.core.handler;
import com.mybatisflex.annotation.EnumValue;
import com.mybatisflex.core.util.ClassUtil;
import org.apache.ibatis.type.EnumTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.lang.reflect.Field;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class CompositeEnumTypeHandler<E extends Enum<E>> implements TypeHandler<E> {
private final TypeHandler<E> delegate;
public CompositeEnumTypeHandler(Class<E> enumClass) {
List<Field> enumDbValueFields = ClassUtil.getAllFields(enumClass, f -> f.getAnnotation(EnumValue.class) != null);
if (enumDbValueFields.isEmpty()) {
delegate = new EnumTypeHandler<>(enumClass);
} else {
delegate = new FlexEnumTypeHandler<>(enumClass);
}
}
@Override
public void setParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
delegate.setParameter(ps, i, parameter, jdbcType);
}
@Override
public E getResult(ResultSet rs, String columnName) throws SQLException {
return delegate.getResult(rs, columnName);
}
@Override
public E getResult(ResultSet rs, int columnIndex) throws SQLException {
return delegate.getResult(rs, columnIndex);
}
@Override
public E getResult(CallableStatement cs, int columnIndex) throws SQLException {
return delegate.getResult(cs, columnIndex);
}
}

View File

@ -0,0 +1,113 @@
/**
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 com.mybatisflex.core.handler;
import com.mybatisflex.annotation.EnumValue;
import com.mybatisflex.core.exception.FlexExceptions;
import com.mybatisflex.core.util.ClassUtil;
import com.mybatisflex.core.util.StringUtil;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class FlexEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
private Class<?> enumPropertyType;
private E[] enums;
private Method getter;
public FlexEnumTypeHandler(Class<E> enumClass) {
List<Field> allFields = ClassUtil.getAllFields(enumClass, field -> field.getAnnotation(EnumValue.class) != null);
Field field = allFields.get(0);
List<Method> allMethods = ClassUtil.getAllMethods(enumClass, method -> {
String methodName = method.getName();
return methodName.equals("get" + StringUtil.firstCharToUpperCase(field.getName()));
});
enumPropertyType = field.getType();
enums = enumClass.getEnumConstants();
getter = allMethods.get(0);
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
Object value = getValue(parameter);
if (jdbcType == null) {
ps.setObject(i, value);
} else {
ps.setObject(i, value, jdbcType.TYPE_CODE);
}
}
private Object getValue(Object object) {
try {
return this.getter.invoke(object);
} catch (Exception e) {
throw FlexExceptions.wrap(e);
}
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
Object value = rs.getObject(columnName, this.enumPropertyType);
if (null == value && rs.wasNull()) {
return null;
}
return convertToEnum(value);
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
Object value = rs.getObject(columnIndex, this.enumPropertyType);
if (null == value && rs.wasNull()) {
return null;
}
return convertToEnum(value);
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
Object value = cs.getObject(columnIndex, this.enumPropertyType);
if (null == value && cs.wasNull()) {
return null;
}
return convertToEnum(value);
}
private E convertToEnum(Object value) {
for (E e : enums) {
if (value.equals(getValue(e))) {
return e;
}
}
return null;
}
}

View File

@ -15,6 +15,7 @@
*/
package com.mybatisflex.core.table;
import com.mybatisflex.core.handler.CompositeEnumTypeHandler;
import com.mybatisflex.core.mask.MaskTypeHandler;
import com.mybatisflex.core.util.StringUtil;
import org.apache.ibatis.type.JdbcType;
@ -51,7 +52,6 @@ public class ColumnInfo {
protected String maskType;
public String getColumn() {
return column;
}
@ -90,6 +90,10 @@ public class ColumnInfo {
if (typeHandler != null) {
return typeHandler;
}
//枚举
else if (propertyType.isEnum()) {
typeHandler = new CompositeEnumTypeHandler(propertyType);
}
//若用户未定义 typeHandler而配置了数据脱敏则使用脱敏的 handler 处理
else if (StringUtil.isNotBlank(maskType)) {
typeHandler = new MaskTypeHandler(maskType);

View File

@ -173,6 +173,7 @@ public class TableInfoFactory {
//未配置 typeHandler 的情况下只支持基本数据类型不支持比如 list set 或者自定义的类等
if ((column == null || column.typeHandler() == UnknownTypeHandler.class)
&& !field.getType().isEnum()
&& !defaultSupportColumnTypes.contains(field.getType())) {
continue;
}

View File

@ -144,22 +144,29 @@ public class ClassUtil {
public static List<Field> getAllFields(Class<?> cl) {
List<Field> fields = new ArrayList<>();
doGetFields(cl, fields);
doGetFields(cl, fields, null);
return fields;
}
public static List<Field> getAllFields(Class<?> cl, Predicate<Field> predicate) {
List<Field> fields = new ArrayList<>();
doGetFields(cl, fields, predicate);
return fields;
}
private static void doGetFields(Class<?> cl, List<Field> fields) {
private static void doGetFields(Class<?> cl, List<Field> fields, Predicate<Field> predicate) {
if (cl == null || cl == Object.class) {
return;
}
Field[] declaredFields = cl.getDeclaredFields();
for (Field declaredField : declaredFields) {
if (predicate == null || predicate.test(declaredField)) {
fields.add(declaredField);
}
}
doGetFields(cl.getSuperclass(), fields);
doGetFields(cl.getSuperclass(), fields, predicate);
}
public static List<Method> getAllMethods(Class<?> cl) {
@ -168,26 +175,26 @@ public class ClassUtil {
return methods;
}
public static List<Method> getAllMethods(Class<?> cl, Predicate<Method> tester) {
public static List<Method> getAllMethods(Class<?> cl, Predicate<Method> predicate) {
List<Method> methods = new ArrayList<>();
doGetMethods(cl, methods, tester);
doGetMethods(cl, methods, predicate);
return methods;
}
private static void doGetMethods(Class<?> cl, List<Method> methods, Predicate<Method> tester) {
private static void doGetMethods(Class<?> cl, List<Method> methods, Predicate<Method> predicate) {
if (cl == null || cl == Object.class) {
return;
}
Method[] declaredMethods = cl.getDeclaredMethods();
for (Method method : declaredMethods) {
if (tester == null || tester.test(method)) {
if (predicate == null || predicate.test(method)) {
methods.add(method);
}
}
doGetMethods(cl.getSuperclass(), methods, tester);
doGetMethods(cl.getSuperclass(), methods, predicate);
}
}