java 使用 morphia 存取枚举为值
源码
https://github.com/zhongchengyi/zhongcy.demos/tree/master/mongo-morphia-demo
前言
morphia是java 使用orm方式操作mongodb的一个库。但是默认情况下,使用morphia存取enum时,是按名字存取的。而我们需要把enum按照值存取。
如图:schoolClassLevel1字段是默认的按enum的name进行存取的,schoolClassLevel是我们想要的(按值存取)。
核心代码
初始化 morphia
Morphia morphia = new Morphia();
try {
Converters converters = morphia.getMapper().getConverters();
Method getEncoder = Converters.class.getDeclaredMethod("getEncoder", Class.class);
getEncoder.setAccessible(true);
TypeConverter enco = ((TypeConverter) getEncoder.invoke(converters, SchoolClassLevel.class));
converters.removeConverter(enco);
converters.addConverter(new EnumOrginalConverter());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
其中, EnumOrginalConverter.java
package zhongcy.demos.converter; import dev.morphia.converters.SimpleValueConverter;
import dev.morphia.converters.TypeConverter;
import zhongcy.demos.util.EnumOriginalProvider;
import zhongcy.demos.util.EnumUtil; public class EnumOrginalConverter extends TypeConverter implements SimpleValueConverter { @Override
@SuppressWarnings({"unchecked", "deprecation"})
public Object decode(final Class targetClass, final Object fromDBObject, final dev.morphia.mapping.MappedField optionalExtraInfo) {
if (fromDBObject == null) {
return null;
} if (hasEnumOriginalProvider(targetClass)) {
return EnumUtil.getEnumObject(Long.parseLong(fromDBObject.toString()), targetClass);
} return Enum.valueOf(targetClass, fromDBObject.toString());
} @Override
@SuppressWarnings({"unchecked", "deprecation"})
public Object encode(final Object value, final dev.morphia.mapping.MappedField optionalExtraInfo) {
if (value == null) {
return null;
} if (hasEnumOriginalProvider(value.getClass())) {
return ((EnumOriginalProvider) value).getIdx();
} return getName(((Enum) value));
} private boolean hasEnumOriginalProvider(Class clzz) {
Class<?>[] interfaces = clzz.getInterfaces();
if (interfaces.length < 1) {
return false;
}
if (interfaces.length == 1) {
return interfaces[0] == EnumOriginalProvider.class;
} else {
for (Class<?> it : interfaces) {
if (it == EnumOriginalProvider.class) {
return true;
}
}
return false;
}
} @Override
@SuppressWarnings({"unchecked", "deprecation"})
protected boolean isSupported(final Class c, final dev.morphia.mapping.MappedField optionalExtraInfo) {
return c.isEnum();
} private <T extends Enum> String getName(final T value) {
return value.name();
}
}
EnumOriginalProvider.java
package zhongcy.demos.util; /**
* enum 的原始数据提供
*/
public interface EnumOriginalProvider { default String getName() {
return null;
} long getIdx();
}
EnumUtil.java
package zhongcy.demos.util; import org.apache.calcite.linq4j.Linq4j;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map; public class EnumUtil { private static EnumUtil instance = new EnumUtil(); Impl impl; EnumUtil() {
impl = new Impl();
} /**
* * 获取value返回枚举对象
* * @param value name 或 index
* * @param clazz 枚举类型
* *
*/
public static <T extends EnumOriginalProvider> T getEnumObject(long idx, Class<T> clazz) {
return instance.impl.getEnumObject(idx, clazz);
} public static <T extends EnumOriginalProvider> T getEnumObject(String name, Class<T> clazz) {
return instance.impl.getEnumObject(name, clazz);
} private class Impl { private Map<Class, EnumFeature[]> enumMap; public Impl() {
enumMap = new HashMap<>();
} public <T extends EnumOriginalProvider> T getEnumObject(long value, Class<T> clazz) {
if (!enumMap.containsKey(clazz)) {
enumMap.put(clazz, createEnumFeatures(clazz));
} try {
EnumFeature first = Linq4j.asEnumerable(enumMap.get(clazz))
.firstOrDefault(f -> value == f.getIndex());
if (first != null) {
return (T) first.getEnumValue();
}
} catch (Exception e) {
}
return null;
} public <T extends EnumOriginalProvider> T getEnumObject(String value, Class<T> clazz) {
if (!enumMap.containsKey(clazz)) {
enumMap.put(clazz, createEnumFeatures(clazz));
} try {
EnumFeature first = Linq4j.asEnumerable(enumMap.get(clazz))
.firstOrDefault(f -> value.equals(f.getName()) || f.getEnumValue().toString().equals(value));
if (first != null) {
return (T) first.getEnumValue();
}
} catch (Exception e) {
}
return null;
} @SuppressWarnings("JavaReflectionInvocation")
private <T extends EnumOriginalProvider> EnumFeature[] createEnumFeatures(Class<T> cls) {
Method method = null;
try {
method = cls.getMethod("values");
return Linq4j.asEnumerable((EnumOriginalProvider[]) method.invoke(null, (Object[]) null))
.select(s -> new EnumFeature(s, s.getName(), s.getIdx())).toList().toArray(new EnumFeature[0]);
} catch (Exception e) {
e.printStackTrace();
return new EnumFeature[0];
}
}
} private class EnumFeature { Object enumValue; String name; long index; public EnumFeature(Object enumValue, String name, long index) {
this.enumValue = enumValue;
this.name = name;
this.index = index;
} public Object getEnumValue() {
return enumValue;
} public String getName() {
return name;
} public long getIndex() {
return index;
}
}
}
morphia简单分析
通过 dev.morphia.DataStoreImpl 的save方法,一路跟踪
到这一步后,查看 TypeConverter 的实现,
找到 EnumConverter,可以看到,morphia 在编码 enum 时,使用的是 enum.getname,我们就想办法替换这个converter.
package dev.morphia.converters; import dev.morphia.mapping.MappedField; /**
* @author Uwe Schaefer, (us@thomas-daily.de)
* @author scotthernandez
*/
public class EnumConverter extends TypeConverter implements SimpleValueConverter { @Override
@SuppressWarnings("unchecked")
public Object decode(final Class targetClass, final Object fromDBObject, final MappedField optionalExtraInfo) {
if (fromDBObject == null) {
return null;
}
return Enum.valueOf(targetClass, fromDBObject.toString());
} @Override
public Object encode(final Object value, final MappedField optionalExtraInfo) {
if (value == null) {
return null;
} return getName((Enum) value);
} @Override
protected boolean isSupported(final Class c, final MappedField optionalExtraInfo) {
return c.isEnum();
} private <T extends Enum> String getName(final T value) {
return value.name();
}
}
Converter替换
查看morphia 的接口,获取 Converters 的方法如下
Converters converters = morphia.getMapper().getConverters();
但是,Converters的接口里面,相关的方法都不是 public..
查看 Converters 的初始化,也发现,初始化路径很长,也不提供设置一个自定义的Converters子类。所以,采取了反射方法,找到enumConvert, 替换成支持返回值得Converter。
即:
Converters converters = morphia.getMapper().getConverters();
Method getEncoder = Converters.class.getDeclaredMethod("getEncoder", Class.class);
getEncoder.setAccessible(true);
TypeConverter enco = ((TypeConverter) getEncoder.invoke(converters, SchoolClassLevel.class));
converters.removeConverter(enco);
converters.addConverter(new EnumOrginalConverter());
EnumOrginalConverter的实现
实现就比较简单了,通过判断enum是否有指定的 interface(EnumOriginalProvider),如果有,encode 方法就返回值。
源码定义了两个枚举:
最终数据库 SchoolClassLevel为值,SchoolClassLevel1为name
其他
java 使用 morphia 存取枚举为值的更多相关文章
- Java枚举-通过值查找对应的枚举
一.背景 Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等. 最近工作中,对接了很多其他的系统,发现对接的同一个系统 ...
- swift_枚举 | 可为空类型 | 枚举关联值 | 枚举递归 | 树的概念
***************可为空的类型 var demo2 :we_demo = nil 上面这个代码串的语法是错的 为什么呢, 在Swift中,所有的类型定义出来的属性的默认值都不可以是nil ...
- [改善Java代码]推荐使用枚举定义常量
枚举和注解都是在Java1.5中引入的,虽然他们是后起之秀,但是功能不容小觑,枚举改变了常量的声明方式,注解耦合了数据和代码. 建议83:推荐使用枚举定义常量 一.分析 常量的声明是每一个项目中不可或 ...
- 浅谈在Java开发中的枚举的作用和用法
枚举(enum),是指一个经过排序的.被打包成一个单一实体的项列表.一个枚举的实例可以使用枚举项列表中任意单一项的值.枚举在各个语言当中都有着广泛的应用,通常用来表示诸如颜色.方式.类别.状态等等数目 ...
- 创建Java不可变型的枚举类型Gender
创建Java不可变型的枚举类型,其实例如下: // 创建不可变型的枚举类 enum Gender { // 此处的枚举值必须调用对应的构造器来创建 MALE("男"), FEMAL ...
- 【Java基础】关于枚举类你可能不知道的事
目录 谈谈枚举 1. 枚举类的定义 2. 枚举类的底层实现 3. 枚举类的序列化实现 4. 用枚举实现单列 5. 枚举实例的创建过程是线程安全的 谈谈枚举 如果一个类的对象个数是有限的而且是不变的,我 ...
- Java基础教程:枚举类型
Java基础教程:枚举类型 枚举类型 枚举是将一具有类似特性的值归纳在一起的方法.比如,我们可以将周一到周日设计为一个枚举类型.彩虹的七种颜色设计为一个枚举类型. 常量实现枚举 我们通过定义常量的方式 ...
- java面向对象程序设计(下)-枚举类
在某些情况下,一个类的对象是有限而且固定的,比如季节类,它只有4个对象;再比如行星类,目前只有8个对象,这些实例有限而且固定的类,在Java中被称为枚举类 JDK1.5新增了一个enum关键字,(它与 ...
- Java当中的内存分配以及值传递问题内存解析
首先必须说明作为Java程序员对于内存只要有大致的了解就可以了,如果你对Java当中的某一个知识点在不需要分析内存分配过程的情况下可以掌握,那就大可不必去研究内存.如果你对知识点已经掌握,那么你应该把 ...
随机推荐
- Spring集成Hessian1
Hessian是一个轻量级的远程调用工具,采用的是Binary RPC协议,很适合于发送二进制数据,基于HTTP具有防火墙穿透能力.Hessian一般是通过Web应用来提供服务,因此非常类似于平时我们 ...
- oralce GROUPING SETS
select id,area,stu_type,sum(score) score from students group by grouping sets((id,area,stu_type),(id ...
- 大侦探福老师——幽灵Crash谜踪案
闲鱼Flutter技术的基础设施已基本趋于稳定,就在我们准备松口气的时候,一个Crash却异军突起冲击着我们的稳定性防线!闲鱼技术火速成立侦探小组执行嫌犯侦查行动,经理重重磨难终于在一个隐蔽的角落将其 ...
- LA 4676 Geometry Problem (几何)
ACM-ICPC Live Archive 又是搞了一个晚上啊!!! 总算是得到一个教训,误差总是会有的,不过需要用方法排除误差.想这题才几分钟,敲这题才半个钟,debug就用了一个晚上了!TAT 有 ...
- Resharper 如何把类里的类移动到其他文件
有时候,看到一个类里有很多类,需要把他移动其他文件 假如有一个类 class A { class B { } } 如何把 B 移动文件 B里? 一般使用 快捷键是 Resharper 的快捷键,如果不 ...
- 阿里云POLARDB荣膺2019中国数据库年度最佳创新产品
在日前的DTCC 2019(第十届中国数据库技术大会)上,阿里云自研云原生数据库POLARDB获选2019中国数据库——“年度最佳创新产品”. POLARDB是阿里云在2018年正式商业化的云原生数据 ...
- 基于TableStore的海量气象格点数据解决方案实战
前言 气象数据是一类典型的大数据,具有数据量大.时效性高.数据种类丰富等特点.气象数据中大量的数据是时空数据,记录了时间和空间范围内各个点的各个物理量的观测量或者模拟量,每天产生的数据量常在几十TB到 ...
- php三目运算计算三个数最大值最小值
文章地址:https://www.cnblogs.com/sandraryan/ $x = 10; $y = 45; $z = 3; //求出三个数字中最大值最小值 //先比较x y,如果x> ...
- dotnet 设计规范 · 抽象定义
严格来说,只有一个类被其他的类继承,那么这个类就是基类.在很多时候,基类的定义是提供足够的抽象和通用方法和属性.默认实现.在继承关系中,基类定义在上层抽象和底层自定义之间. 他们充当抽象实现的实现帮助 ...
- Linux下FTP的安装和登陆
对于一个经常接触电脑的人来说,FTP无形中出现在我们生活的各个角落.日常生活中的文件上传和下载很多时候就是依靠FTP去实现的. 专业的说,FTP 是File Transfer Protocol(文件传 ...