morphia java_java 使用 morphia 存取枚举为值

源码

前言

morphia是java 使用orm方式操作mongodb的一个库。但是默认情况下,使用morphia存取enum时,是按名字存取的。而我们需要把enum按照值存取。

如图:schoolClassLevel1字段是默认的按enum的name进行存取的,schoolClassLevel是我们想要的(按值存取)。

fc211709b1749e8f7ce62f1e4d708b17.png

核心代码

初始化 morphia

Morphia morphia = newMorphia();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(newEnumOrginalConverter());

}catch(NoSuchMethodException e) {

e.printStackTrace();

}catch(IllegalAccessException e) {

e.printStackTrace();

}catch(InvocationTargetException e) {

e.printStackTrace();

}

其中, EnumOrginalConverter.java

packagezhongcy.demos.converter;importdev.morphia.converters.SimpleValueConverter;importdev.morphia.converters.TypeConverter;importzhongcy.demos.util.EnumOriginalProvider;importzhongcy.demos.util.EnumUtil;public class EnumOrginalConverter extends TypeConverter implementsSimpleValueConverter {

@Override

@SuppressWarnings({"unchecked", "deprecation"})public Object decode(final Class targetClass, final Object fromDBObject, finaldev.morphia.mapping.MappedField optionalExtraInfo) {if (fromDBObject == null) {return null;

}if(hasEnumOriginalProvider(targetClass)) {returnEnumUtil.getEnumObject(Long.parseLong(fromDBObject.toString()), targetClass);

}returnEnum.valueOf(targetClass, fromDBObject.toString());

}

@Override

@SuppressWarnings({"unchecked", "deprecation"})public Object encode(final Object value, finaldev.morphia.mapping.MappedField optionalExtraInfo) {if (value == null) {return null;

}if(hasEnumOriginalProvider(value.getClass())) {return((EnumOriginalProvider) value).getIdx();

}returngetName(((Enum) value));

}private booleanhasEnumOriginalProvider(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, finaldev.morphia.mapping.MappedField optionalExtraInfo) {returnc.isEnum();

}private String getName(finalT value) {returnvalue.name();

}

}

EnumOriginalProvider.java

packagezhongcy.demos.util;/*** enum 的原始数据提供*/

public interfaceEnumOriginalProvider {defaultString getName() {return null;

}longgetIdx();

}

EnumUtil.java

packagezhongcy.demos.util;importorg.apache.calcite.linq4j.Linq4j;importjava.lang.reflect.Method;importjava.util.HashMap;importjava.util.Map;public classEnumUtil {private static EnumUtil instance = newEnumUtil();

Impl impl;

EnumUtil() {

impl= newImpl();

}/*** * 获取value返回枚举对象

* *@paramvalue name 或 index

* *@paramclazz 枚举类型

* **/

public static T getEnumObject(long idx, Classclazz) {returninstance.impl.getEnumObject(idx, clazz);

}public static T getEnumObject(String name, Classclazz) {returninstance.impl.getEnumObject(name, clazz);

}private classImpl {private MapenumMap;publicImpl() {

enumMap= new HashMap<>();

}public T getEnumObject(long value, Classclazz) {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 getEnumObject(String value, Classclazz) {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 EnumFeature[] createEnumFeatures(Classcls) {

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 classEnumFeature {

Object enumValue;

String name;longindex;public EnumFeature(Object enumValue, String name, longindex) {this.enumValue =enumValue;this.name =name;this.index =index;

}publicObject getEnumValue() {returnenumValue;

}publicString getName() {returnname;

}public longgetIndex() {returnindex;

}

}

}

morphia简单分析

通过 dev.morphia.DataStoreImpl 的save方法,一路跟踪

0b34a7abe6180e87eba6573a6a315104.png

3b025952176300666b93818bb3054753.png

到这一步后,查看 TypeConverter 的实现,

9b3b31f1cd28c36ab3c354848585a2f2.png

找到 EnumConverter,可以看到,morphia 在编码 enum 时,使用的是 enum.getname,我们就想办法替换这个converter.

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagedev.morphia.converters;importdev.morphia.mapping.MappedField;/***@authorUwe Schaefer, (us@thomas-daily.de)

*@authorscotthernandez*/

public class EnumConverter extends TypeConverter implementsSimpleValueConverter {

@Override

@SuppressWarnings("unchecked")public Object decode(final Class targetClass, final Object fromDBObject, finalMappedField optionalExtraInfo) {if (fromDBObject == null) {return null;

}returnEnum.valueOf(targetClass, fromDBObject.toString());

}

@Overridepublic Object encode(final Object value, finalMappedField optionalExtraInfo) {if (value == null) {return null;

}returngetName((Enum) value);

}

@Overrideprotected boolean isSupported(final Class c, finalMappedField optionalExtraInfo) {returnc.isEnum();

}private String getName(finalT value) {returnvalue.name();

}

}

View Code

Converter替换

查看morphia 的接口,获取 Converters 的方法如下

Converters converters = morphia.getMapper().getConverters();

但是,Converters的接口里面,相关的方法都不是 public..

c9c1dc8c2120fcc7cf46e5263fd61bdb.png

查看 Converters 的初始化,也发现,初始化路径很长,也不提供设置一个自定义的Converters子类。所以,采取了反射方法,找到enumConvert, 替换成支持返回值得Converter。

即:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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());

View Code

EnumOrginalConverter的实现

实现就比较简单了,通过判断enum是否有指定的 interface(EnumOriginalProvider),如果有,encode 方法就返回值。

源码定义了两个枚举:

6d988412f52e49dd00cf5b338c2bc800.png

e130eeb5cfacca82b5ff74974b055e61.png

最终数据库 SchoolClassLevel为值,SchoolClassLevel1为name

8d7b8bf92f9971b4305f77914da54970.png

其他

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值