Browse Source

support normal enum field

yuxiaobin 6 years ago
parent
commit
3e3570b5c8

+ 41 - 0
mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableFieldEnumValue.java

@@ -0,0 +1,41 @@
+package com.baomidou.mybatisplus.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * 支持普通枚举类字段, 只用在enum类的字段上
+ *
+ * 当实体类的属性是普通枚举,且是其中一个字段,使用该注解来标注枚举类里的那个属性对应字段
+ *
+ * class Student{
+ *     private Integer id;
+ *     private String name;
+ *     private GradeEnum grade;
+ * }
+ *
+ * public enum GradeEnum{
+ *     PRIMARY(1,"小学"),
+ *     SECONDORY("2", "中学"),
+ *     HIGH(3, "高中");
+ *
+ *      @TableFieldEnumValue
+ *      private final int code;
+ *      private final String descp;
+ *
+ * }
+ * </p>
+ *
+ * @author yuxiaobin
+ * @date 2018/8/30
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface TableFieldEnumValue {
+
+}

+ 16 - 7
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/EnumUtils.java

@@ -16,6 +16,9 @@
 package com.baomidou.mybatisplus.core.toolkit;
 
 
+import java.lang.reflect.Field;
+import java.util.Objects;
+
 import com.baomidou.mybatisplus.core.enums.IEnum;
 
 /**
@@ -41,16 +44,22 @@ public class EnumUtils {
     public static <E extends Enum<?> & IEnum> E valueOf(Class<E> enumClass, Object value) {
         E[] es = enumClass.getEnumConstants();
         for (E e : es) {
-            if (e.getValue() == value) {
-                // 基本类型
+            if (Objects.equals(e.getValue(), value)) {
                 return e;
-            } else if (value instanceof Number) {
-                if (e.getValue() instanceof Number &&
-                    ((Number) value).doubleValue() == ((Number) e.getValue()).doubleValue()) {
+            }
+        }
+        return null;
+    }
+
+    public static <E extends Enum<?>> E valueOf(Class<E> enumClass, Object value, Field enumField) {
+        E[] es = enumClass.getEnumConstants();
+        for (E e : es) {
+            try {
+                if (Objects.equals(enumField.get(e), value)) {
                     return e;
                 }
-            } else if (String.valueOf(value).equals(String.valueOf(e.getValue()))) {
-                return e;
+            } catch (IllegalAccessException e1) {
+
             }
         }
         return null;

+ 86 - 0
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/handlers/EnumAnnotationTypeHandler.java

@@ -0,0 +1,86 @@
+package com.baomidou.mybatisplus.extension.handlers;
+
+import java.lang.reflect.Field;
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+
+import com.baomidou.mybatisplus.core.toolkit.EnumUtils;
+
+/**
+ * <p>
+ * 支持
+ * </p>
+ *
+ * @author yuxiaobin
+ * @date 2018/8/30
+ */
+public class EnumAnnotationTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
+
+    private final Class<E> type;
+
+    public EnumAnnotationTypeHandler(Class<E> type) {
+        if (type == null) {
+            throw new IllegalArgumentException("Type argument cannot be null");
+        }
+        this.type = type;
+    }
+
+    private static final Log LOGGER = LogFactory.getLog(EnumAnnotationTypeHandler.class);
+
+    private static final Map<Class<?>, Field> TABLE_FIELD_OF_ENUM_TYPES = new ConcurrentHashMap<>();
+
+    public static void addEnumType(Class<?> clazz, Field tableFieldOfEnumType) {
+        TABLE_FIELD_OF_ENUM_TYPES.put(clazz, tableFieldOfEnumType);
+    }
+
+
+    @Override
+    public void setNonNullParameter(PreparedStatement ps, int i, Enum parameter, JdbcType jdbcType) throws SQLException {
+        Field tableEnumField = TABLE_FIELD_OF_ENUM_TYPES.get(type);
+        try {
+            if (jdbcType == null) {
+                ps.setObject(i, tableEnumField == null ? parameter.name() : tableEnumField.get(parameter));
+            } else {
+                ps.setObject(i, tableEnumField == null ? parameter.name() : tableEnumField.get(parameter), jdbcType.TYPE_CODE); // see r3589
+            }
+        } catch (IllegalAccessException e) {
+            LOGGER.error("unrecognized jdbcType, failed to set StringValue for type=" + parameter);
+        }
+    }
+
+    @Override
+    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
+        return getEnumResult(rs.getObject(columnName));
+    }
+
+    private E getEnumResult(Object s) {
+        if (s == null) {
+            return null;
+        }
+        Field tableEnumField = TABLE_FIELD_OF_ENUM_TYPES.get(type);
+        if (tableEnumField != null) {
+            return EnumUtils.valueOf(type, s, tableEnumField);
+        }
+        return Enum.valueOf(type, s.toString());
+    }
+
+    @Override
+    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+        return getEnumResult(rs.getObject(columnIndex));
+    }
+
+    @Override
+    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+        return getEnumResult(cs.getString(columnIndex));
+    }
+
+}

+ 38 - 14
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/spring/MybatisSqlSessionFactoryBean.java

@@ -22,6 +22,7 @@ import static org.springframework.util.StringUtils.hasLength;
 import static org.springframework.util.StringUtils.tokenizeToStringArray;
 
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.ArrayList;
@@ -63,6 +64,7 @@ import org.springframework.core.io.Resource;
 import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
 
 import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.annotation.TableFieldEnumValue;
 import com.baomidou.mybatisplus.core.MybatisConfiguration;
 import com.baomidou.mybatisplus.core.MybatisXMLConfigBuilder;
 import com.baomidou.mybatisplus.core.config.GlobalConfig;
@@ -74,6 +76,8 @@ import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
 import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
 import com.baomidou.mybatisplus.core.toolkit.StringPool;
 import com.baomidou.mybatisplus.core.toolkit.sql.SqlHelper;
+import com.baomidou.mybatisplus.extension.handlers.EnumAnnotationTypeHandler;
+import com.baomidou.mybatisplus.extension.handlers.EnumTypeHandler;
 import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
 import com.baomidou.mybatisplus.extension.toolkit.PackageHelper;
 
@@ -448,9 +452,9 @@ public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFacto
             List<String> typeAliasPackageList = new ArrayList<>();
             if (typeAliasesPackage.contains(StringPool.ASTERISK) && !typeAliasesPackage.contains(StringPool.COMMA) && !typeAliasesPackage.contains(StringPool.SEMICOLON)) {
                 String[] convertTypeAliasesPackages = PackageHelper.convertTypeAliasesPackage(this.typeAliasesPackage);
-                if(ArrayUtils.isEmpty(convertTypeAliasesPackages)){
-                    LOGGER.warn("Can't find class in '["+this.typeAliasesPackage+"]' package. Please check your configuration.");
-                }else{
+                if (ArrayUtils.isEmpty(convertTypeAliasesPackages)) {
+                    LOGGER.warn("Can't find class in '[" + this.typeAliasesPackage + "]' package. Please check your configuration.");
+                } else {
                     typeAliasPackageList.addAll(Arrays.asList(convertTypeAliasesPackages));
                 }
             } else {
@@ -458,9 +462,9 @@ public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFacto
                 for (String one : typeAliasPackageArray) {
                     if (one.contains(StringPool.ASTERISK)) {
                         String[] convertTypeAliasesPackages = PackageHelper.convertTypeAliasesPackage(one);
-                        if(ArrayUtils.isEmpty(convertTypeAliasesPackages)){
-                            LOGGER.warn("Can't find class in '["+one+"]' package. Please check your configuration.");
-                        }else {
+                        if (ArrayUtils.isEmpty(convertTypeAliasesPackages)) {
+                            LOGGER.warn("Can't find class in '[" + one + "]' package. Please check your configuration.");
+                        } else {
                             typeAliasPackageList.addAll(Arrays.asList(convertTypeAliasesPackages));
                         }
                     } else {
@@ -483,8 +487,8 @@ public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFacto
             if (typeEnumsPackage.contains(StringPool.STAR) && !typeEnumsPackage.contains(StringPool.COMMA)
                 && !typeEnumsPackage.contains(StringPool.SEMICOLON)) {
                 classes = PackageHelper.scanTypePackage(typeEnumsPackage);
-                if(classes.isEmpty()){
-                    LOGGER.warn("Can't find class in '["+typeEnumsPackage+"]' package. Please check your configuration.");
+                if (classes.isEmpty()) {
+                    LOGGER.warn("Can't find class in '[" + typeEnumsPackage + "]' package. Please check your configuration.");
                 }
             } else {
                 String[] typeEnumsPackageArray = tokenizeToStringArray(this.typeEnumsPackage,
@@ -493,9 +497,9 @@ public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFacto
                 classes = new HashSet<>();
                 for (String typePackage : typeEnumsPackageArray) {
                     Set<Class> scanTypePackage = PackageHelper.scanTypePackage(typePackage);
-                    if(scanTypePackage.isEmpty()){
-                        LOGGER.warn("Can't find class in '["+typePackage+"]' package. Please check your configuration.");
-                    }else{
+                    if (scanTypePackage.isEmpty()) {
+                        LOGGER.warn("Can't find class in '[" + typePackage + "]' package. Please check your configuration.");
+                    } else {
                         classes.addAll(PackageHelper.scanTypePackage(typePackage));
                     }
                 }
@@ -505,10 +509,11 @@ public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFacto
             for (Class cls : classes) {
                 if (cls.isEnum()) {
                     if (IEnum.class.isAssignableFrom(cls)) {
-                        typeHandlerRegistry.register(cls.getName(), com.baomidou.mybatisplus.extension.handlers.EnumTypeHandler.class.getCanonicalName());
+                        typeHandlerRegistry.register(cls, EnumTypeHandler.class);
                     } else {
-                        // 使用原生 EnumOrdinalTypeHandler
-                        typeHandlerRegistry.register(cls.getName(), org.apache.ibatis.type.EnumOrdinalTypeHandler.class.getCanonicalName());
+                        //使用原生 EnumOrdinalTypeHandler
+                        dealEnumType(cls);
+                        typeHandlerRegistry.register(cls, EnumAnnotationTypeHandler.class);
                     }
                 }
             }
@@ -636,6 +641,25 @@ public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFacto
         return sqlSessionFactory;
     }
 
+    /**
+     * 处理普通枚举
+     * 把带{@link TableFieldEnumValue}的field注册到处理器中
+     *
+     * @param clazz
+     */
+    protected void dealEnumType(Class<?> clazz) {
+        if (clazz.isEnum()) {
+            Field[] fields = clazz.getDeclaredFields();
+            for (Field f : fields) {
+                if (f.isAnnotationPresent(TableFieldEnumValue.class)) {
+                    f.setAccessible(true);
+                    EnumAnnotationTypeHandler.addEnumType(clazz, f);
+                    return;
+                }
+            }
+        }
+    }
+
     /**
      * {@inheritDoc}
      */

+ 76 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2StudentMapperTest.java

@@ -0,0 +1,76 @@
+package com.baomidou.mybatisplus.test.h2;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Objects;
+
+import javax.annotation.Resource;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Assert;
+import com.baomidou.mybatisplus.test.h2.config.H2Db;
+import com.baomidou.mybatisplus.test.h2.entity.enums.GenderEnum;
+import com.baomidou.mybatisplus.test.h2.entity.enums.GradeEnum;
+import com.baomidou.mybatisplus.test.h2.entity.mapper.H2StudentMapper;
+import com.baomidou.mybatisplus.test.h2.entity.persistent.H2Student;
+
+/**
+ * <p>
+ * Mybatis Plus H2 Junit Test
+ * </p>
+ *
+ * @author hubin
+ * @since 2018-06-05
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {"classpath:h2/spring-test-h2.xml"})
+public class H2StudentMapperTest extends BaseTest {
+
+    @Resource
+    protected H2StudentMapper studentMapper;
+
+    @BeforeClass
+    public static void InitDB() throws SQLException, IOException {
+        H2Db.initH2Student();
+    }
+
+    @Test
+    public void crudTest() {
+//        H2Student stu = new H2Student();
+//        stu.setGrade(GradeEnum.HIGH);
+//        studentMapper.update(stu, null);
+        H2Student student = new H2Student();
+        Long id = 10086L;
+        student.setId(id);
+        student.setAge(188);
+        student.setGender(GenderEnum.MALE);
+        student.setGrade(GradeEnum.PRIMARY);
+        studentMapper.insert(student);
+
+        List<H2Student> list = studentMapper.selectList(new QueryWrapper<>());
+        for (H2Student s : list) {
+            System.out.println(s.getGrade());
+            if (Objects.equals(s.getId(), id)) {
+                Assert.notNull(s.getGrade(), "id=" + id + " should have grade");
+                Assert.notNull(s.getGender(), "id=" + id + " should have gender");
+            }
+        }
+        H2Student updateStu = new H2Student();
+        updateStu.setId(15L);
+        updateStu.setGrade(GradeEnum.HIGH);
+        updateStu.setGender(GenderEnum.FEMALE);
+        Assert.isTrue(studentMapper.updateById(updateStu) == 1, "should update success");
+        updateStu = studentMapper.selectById(15L);
+        Assert.notNull(updateStu.getGrade(), "grade should updated");
+        Assert.notNull(updateStu.getGender(), "gender should updated");
+
+    }
+
+}

+ 14 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/enums/GenderEnum.java

@@ -0,0 +1,14 @@
+package com.baomidou.mybatisplus.test.h2.entity.enums;
+
+/**
+ * <p>
+ * </p>
+ *
+ * @author yuxiaobin
+ * @date 2018/8/30
+ */
+public enum GenderEnum {
+
+    MALE,
+    FEMALE;
+}

+ 35 - 0
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/enums/GradeEnum.java

@@ -0,0 +1,35 @@
+package com.baomidou.mybatisplus.test.h2.entity.enums;
+
+import com.baomidou.mybatisplus.annotation.TableFieldEnumValue;
+
+/**
+ * <p>
+ * </p>
+ *
+ * @author yuxiaobin
+ * @date 2018/8/30
+ */
+public enum GradeEnum {
+
+    PRIMARY(1, "小学"),
+    SECONDARY(2, "中学"),
+    HIGH(3, "高中");
+
+    GradeEnum(int code, String descp) {
+        this.code = code;
+        this.descp = descp;
+    }
+
+    @TableFieldEnumValue
+    private final int code;
+
+    private final String descp;
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getDescp() {
+        return descp;
+    }
+}

+ 17 - 2
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/entity/persistent/H2Student.java

@@ -1,18 +1,22 @@
 package com.baomidou.mybatisplus.test.h2.entity.persistent;
 
+import java.io.Serializable;
+
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.baomidou.mybatisplus.test.h2.entity.enums.GenderEnum;
+import com.baomidou.mybatisplus.test.h2.entity.enums.GradeEnum;
+
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
 
-import java.io.Serializable;
-
 /**
  * 学生实体
+ *
  * @author nieqiurong 2018/7/27.
  */
 @Data
@@ -22,11 +26,22 @@ import java.io.Serializable;
 @EqualsAndHashCode(callSuper = true)
 public class H2Student extends Model<H2Student> {
 
+
+    public H2Student(Long id, String name, Integer age) {
+        this.id = id;
+        this.name = name;
+        this.age = age;
+    }
+
     @TableId(type = IdType.AUTO)
     private Long id;
 
     private String name;
 
+    private GradeEnum grade;
+
+    private GenderEnum gender;
+
     private Integer age;
 
     @Override

+ 2 - 0
mybatis-plus/src/test/resources/h2/student.ddl.sql

@@ -1,6 +1,8 @@
 CREATE TABLE IF NOT EXISTS  h2student (
 	id BIGINT(20) NOT NULL AUTO_INCREMENT,
 	name VARCHAR(30) NULL DEFAULT NULL ,
+	grade INT(3) NULL,
+	gender VARCHAR(10) NULL,
 	age INT(11) NULL DEFAULT NULL ,
 	PRIMARY KEY (id)
 )