Title

java 使用 enum(枚举)

枚举的使用

  • 枚举类的理解:类的对象只有有限个,确定的。我们称此类为枚举类
  • 当需要定义一组常量时,强烈建议使用枚举类
  • 如果枚举类中只有一个对象,则可以作为单例模式的实现方式。

枚举的属性

  • 枚举类对象的属性不应允许被改动,所以应该使用 private final 修饰
  • 枚举类的使用 private final 修饰的属性应该在构造器中为其赋值
  • 若枚举类显式的定义了带参数的构造器,则在列出枚举值时也必须对应的传入参数

测试

package com.test;

/**
 * @author z
 */
public class Test {
    public static void main(String[] args) {
        // code
        System.out.println(MyEnums.MY_TEST_ONE.getCode());
        // value
        System.out.println(MyEnums.MY_TEST_ONE.getValue());
        // 获取变量名列表
        for(MyEnums myEnums:MyEnums.getVariables()){
            System.out.println(myEnums.toString());
        }
        // 根据 key 获取 value
        System.out.println(MyEnums.getValue(2));
    }
}
public enum MyEnums{
    MY_TEST_ONE(1,"测试1"),
    MY_TEST_TWO(2,"测试2"),
    MY_TEST_three(3,"测试3"),
    ;
    private final int code;
    private final String value;
    MyEnums(int code,String value){
        this.code=code;
        this.value=value;
    }

    /**
     * 获取 code
     */
    public int getCode(){
        return this.code;
    }

    /**
     * 获取 value
     */
    public String getValue(){
        return this.value;
    }

    /**
     * 获取变量名列表
     */
    public static MyEnums[] getVariables(){
        return values();
    }

    /**
     * 根据code获取value
     */
    public static String getValue(int code){
        for(MyEnums myEnums:values()){
            if(code==myEnums.code){
                return myEnums.value;
            }
        }
        return "";
    }

    /**
     * 枚举转List
     */
    public static List<HashMap<String, String>> getAllTypeMap() {
        List<HashMap<String, String>> list = new ArrayList<>();
        for (MyEnums myEnums : EnumSet.allOf(MyEnums.class)) {
            HashMap<String, String> map = new HashMap<>();
            map.put("name", myEnums.value);
            map.put("code", myEnums.code);
            list.add(map);
        }
        return list;
    }
}

结果:

实体类使用枚举做映射

我们在使用ORM框架的时候,经常会碰到这种情况,比如数据库中有一个tinyint或char类型的gender字段,映射成为实体对象时倾向使用GenderEnum这样的枚举类型,因为这样使用起来比较方便,否则我们就需要手动转换枚举类型,如果这样的字段比较多,这样的手动转换还是比较费时费力的。如果我们使用的时MybatisPlus,那么处理这样的情况就比较方便。

处理方案

配置文件中(比如application.properties)配置枚举类型的扫描路径

mybatis-plus:
	type-enums-package: com.zl.backages

枚举类中添加@EnumValue注解

package com.example.webdemo.enumeration;

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum GenderEnum {

    MALE("M", "男性"),
    FEMALE("F", "女性");

    @EnumValue
    private String code;
    @JsonValue
    private String codeDesc;
}

映射实体类

package com.example.webdemo.entity;

import com.example.webdemo.enumeration.GenderEnum;
import lombok.Data;

@Data
public class User {

    private Long id;
    private String name;
    private Integer age;
    private GenderEnum gender;
}

数据库中字段

CREATE TABLE `t_user` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int DEFAULT NULL,
  `gender` char(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
原理

我们知道,MybatisPlus是对Mybatis的增强,底层还是会依赖到Mybatis的相关模块,而且MybatisPlus的很多类都跟Mybatis的很像。Springboot框架下使用Mybatis时,通过MybatisAutoConfiguration配置类知道,自动装配了两个重要的对象,SqlSessionFactoryBean以及SqlSessionTemplate对象。相对应的,集成MybatisPlus时,也有两个类似的对象,MybatisSqlSessionFactoryBean以及SqlSessionTemplate对象(具体可以参照MybatisPlusAutoConfiguration配置类)。

MybatisPlusAutoConfiguration#sqlSessionFactory

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        // TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        // 省略部分代码
        // TODO 自定义枚举包
        if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
            factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
        }
        // 省略部分代码
        return factory.getObject();
    }

如果MybatisPlus配置了枚举类扫描路径(最终会映射到MybatisPlusProperties文件中),设置到MybatisSqlSessionFactoryBean对象中,后续生成SqlSessionFactory时会有相关的处理。

MybatisSqlSessionFactoryBean#getObject

-->MybatisSqlSessionFactoryBean#afterPropertiesSet

-->MybatisSqlSessionFactoryBean#buildSqlSessionFactory

    protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
	MybatisXMLConfigBuilder xmlConfigBuilder = null;
	Object targetConfiguration;
	// 省略部分代码
	if (StringUtils.hasLength(this.typeEnumsPackage)) {
		Object classes;
		if (this.typeEnumsPackage.contains("*") && !this.typeEnumsPackage.contains(",") && !this.typeEnumsPackage.contains(";")) {
			classes = this.scanClasses(this.typeEnumsPackage, (Class)null);
			if (((Set)classes).isEmpty()) {
				LOGGER.warn(() -> {
					return "Can't find class in '[" + this.typeEnumsPackage + "]' package. Please check your configuration.";
				});
			}
		} else {
			classes = new HashSet();
			String[] typeEnumsPackageArray = StringUtils.tokenizeToStringArray(this.typeEnumsPackage, ",; \t\n");
			com.baomidou.mybatisplus.core.toolkit.Assert.notNull(typeEnumsPackageArray, "not find typeEnumsPackage:" + this.typeEnumsPackage, new Object[0]);
			Stream.of(typeEnumsPackageArray).forEach((typePackage) -> {
				try {
					Set<Class<?>> scanTypePackage = this.scanClasses(typePackage, (Class)null);
					if (scanTypePackage.isEmpty()) {
						LOGGER.warn(() -> {
							return "Can't find class in '[" + typePackage + "]' package. Please check your configuration.";
						});
					} else {
						classes.addAll(scanTypePackage);
					}

				} catch (IOException var4) {
					throw new MybatisPlusException("Cannot scan class in '[" + typePackage + "]' package", var4);
				}
			});
		}

		TypeHandlerRegistry typeHandlerRegistry = ((Configuration)targetConfiguration).getTypeHandlerRegistry();
		((Set)classes).stream().filter(Class::isEnum).filter(MybatisEnumTypeHandler::isMpEnums).forEach((cls) -> {
			typeHandlerRegistry.register(cls, MybatisEnumTypeHandler.class);
		});
	}
	// 省略部分代码
	return sqlSessionFactory;
    }

如果配置了枚举类扫描路径,MybatisPlus会获取该路径下所有的枚举类,遍历为每一个枚举类注册一个MybatisEnumTypeHandler对象到TypeHandlerRegistry中。熟悉Mybatis的同学都知道,TypeHandler对象在ParameterHandler设置参数或者ResultSetHandler映射对象时会被调用。MybatisEnumTypeHandler怎么处理枚举相关逻辑自行查看源码。

参考链接:
https://zhuanlan.zhihu.com/p/456010202

posted @ 2022-05-20 18:12  快乐小洋人  阅读(1659)  评论(0编辑  收藏  举报