• 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();
        }
    }
    View Code

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

    EnumOrginalConverter的实现

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

    代码见上面(EnumOrginalConverter)

    源码定义了两个枚举:

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

     其他

      

  • 相关阅读:
    DIY 作品 及 维修 不定时更新
    置顶,博客中所有源码 github
    openwrt PandoraBox PBR-M1 极路由4 HC5962 更新固件
    使用 squid 共享 虚拟专用网至局域网
    第一次参加日语能力测试 N5
    libx264 libfdk_aac 编码 解码 详解
    开发RTSP 直播软件 H264 AAC 编码 live555 ffmpeg
    MFC Camera 摄像头预览 拍照
    http2 技术整理 nginx 搭建 http2 wireshark 抓包分析 server push 服务端推送
    plist 图集 php 批量提取 PS 一个个切
  • 原文地址:https://www.cnblogs.com/zhongchengyi/p/12125014.html
Copyright © 2020-2023  润新知