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怎么处理枚举相关逻辑自行查看源码。