java 使用 morphia 存取枚舉為值

来源:https://www.cnblogs.com/zhongchengyi/archive/2019/12/31/12125014.html
-Advertisement-
Play Games

前言 morphia是java 使用orm方式操作mongodb的一個庫。但是預設情況下,使用morphia存取enum時,是按名字存取的。而我們需要把enum按照值存取。 如圖:schoolClassLevel1欄位是預設的按enum的name進行存取的,schoolClassLevel是我們想要 ...


前言

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, ([email protected])
 * @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

 

源碼

https://github.com/zhongchengyi/zhongcy.demos/tree/master/mongo-morphia-demo

其他

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Talk is cheap, show me the code! 以上這段mybatis的入門案例代碼,相信每一個瞭解mybatis的朋友都能看得懂,知碼醬同學今天也細細品了品! 1. 項目的路徑問題 : 在實際的項目中,並不推薦眾所周知的相對路徑和絕對路徑。 相對路徑: web應用是需要部署到服務 ...
  • 一、生成表格1.創建模型類(在 models.py文件中創建一個person類並且繼承models.Models類) 2.生成表格(在項目目錄下)(1)生成遷移文件:在pycharm下方的命令行Terminal中寫入python manage.py makemigrations,回車鍵後顯示遷移文件 ...
  • Mapper代理 "上一節" 中直接利用session+id來執行sql的方式存在一些問題 session執行sql時都需要提供要執行sql的id,而這個id是字元串類型,意味著id是否正確在編譯期間是無法獲知的,必須等到運行時才能發現錯誤, sql需要的參數和返回值類都不明確,這也增加了出錯的概率 ...
  • 傳值還是傳引用 調用函數時, 傳入的參數的 傳值 還是 傳引用 , 幾乎是每種編程語言都會關註的問題. 最近在使用 golang 的時候, 由於 傳值 和 傳引用 的方式沒有弄清楚, 導致了 BUG. 經過深入的嘗試, 終於弄明白了 golang 的 傳值 的 傳引用 , 嘗試過程記錄如下, 供大家 ...
  • 1. Maven綜述與拓展概念準備 Maven起源:生產環境下開發不再是一個項目一個工程,而是每一個模塊創建一個工程, 而多個模塊整合在一起就需要使用到像 Maven 這樣的構建工具。 Maven定義:一個自動化構建工具 Maven簡介:Maven是Apache軟體基金會組織維護的一款自動化構建工具 ...
  • 效果 修改步驟 Settings -> Editor -> Code Style -> Java ...
  • 黑馬客戶管理系統 1系統概述 1.1系統功能介紹 本系統後臺使用SSM框架編寫,前臺頁面使用當前主流的Bootstrap和jQuery框架完成頁面信息展示功能(關於Bootstrap的知識,有興趣的讀者可參考黑馬程式員編著的《響應式Web開發項目教程》)。系統中主要實現了兩大功能模塊:用戶登錄模塊和 ...
  • 我將 Visual Studio Code 作為Rust首選編輯器。遺憾的是 VS Code 不能非常好地完成 Rust 的調試。 配置調試器不難,但仍然需要幾個步驟。我已經完整配置了好幾次。我正在寫這個指南,以方便我以後查閱,而不必非要記住詳細的安裝步驟。 希望這個指南對老鐵們也能有些幫助,要記得 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...