首頁 > Java > Java基礎 > 主體

Java使用反射,把物件轉換成 MongoDb 的結構

coldplay.xixi
發布: 2020-12-15 17:30:48
轉載
3741 人瀏覽過

java基礎教學專欄介紹如何把物件轉換成MongoDb的結構

Java使用反射,把物件轉換成 MongoDb 的結構

#推薦(免費):java基礎教學

反射是Java 的進階技巧,大量地用在各種開源專案上。例如,Spring、Tomcat、Jetty 等等專案中,都大量地用到了反射。

身為 Java 程式設計師,我們如果用好反射,不但能提升自己的技術水平,還能開發出更好的專案。

然而,雖然很多人聽過反射,但卻不知道該用在哪裡。

那麼,我們就從實際工作出發,使用反射,把物件轉換成 MongoDb 的資料結構。當你在搞懂這個例子後,你就能明白反射是怎麼個用法。

需求分析

在電商系統中,有些資料要儲存到 MongoDb 中,以此來提高查詢的效能。但在此之前,我們必須先把資料轉換成 MongoDb 的結構,也就是把 Java 物件轉換成 Document。

例如,訂單資訊要存到 MongoDb 中,就得把訂單物件轉換成 Document。

可這樣一來,每個實體類別都得發展一個 2Doc() 方法。這個方法毫無技術含量,就是把各種欄位 put 到 Document 裡面。而且一旦字段多了,一不留神就會寫錯代碼,你感受一下。

public class Order {
    private Long id;
    private Long userId;
    private String orderNo;
    private BigDecimal amount;
    private String createTime;
    private String updateTime;
    // 省略无数字段
    
    // 转换方法:订单转doc
    public Document order2Doc(Order order) {
        Document doc = new Document();

        doc.put("id", order.getId());
        doc.put("userId", order.getUserId());
        doc.put("orderNo", order.getOrderNo());
        doc.put("amount", order.getAmount());
        doc.put("createTime", order.getCreateTime());
        doc.put("updateTime", order.getUpdateTime());
        // 省略无数put...

        return doc;
    }
}
登入後複製

除此之外,我們還得從 MongoDb 中取數據,把 Document 轉換回 Java 對象,你再感受一下。

public class Order {
    private Long id;
    private Long userId;
    private String orderNo;
    private BigDecimal amount;
    private String createTime;
    private String updateTime;
    // 省略无数字段
    
    // 转换方法:doc转订单
    public Order doc2Order(Document doc) {
        Order order = new Order();
        
        order.setId((Long) doc.get("id"));
        order.setUserId((Long) doc.get("userId"));
        order.setOrderNo((String) doc.get("orderNo"));
        order.setAmount((BigDecimal) doc.get("amount"));
        order.setCreateTime((String) doc.get("createTime"));
        order.setUpdateTime((String) doc.get("updateTime"));
        // 省略无数set...

        return order;
    }
}
登入後複製

光是一個訂單類別都這麼麻煩了,何況這樣的類別不只一個,而且專案總有新需求,如果一個字段改了,那你麻煩大了,說不定要把整個專案翻一遍。

因此,為了少出錯,必須優化這兩個轉換方法,而這次優化用到了 Java 的兩個高階特性:反射、泛型。為了讓大家更直覺的了解,我將分成兩個版本迭代。

第一版,利用反射,簡化實體類別的轉換方法;

第二版,利用泛型、反射,提取MongoDb 工具類別;

接下來,我們就一步步迭代吧~

利用反射,簡化實體類別的轉換方法

在第一版的迭代中,我們要簡化實體類別的兩個轉換方法。

我們先從 Java 物件轉 Document 開始,還是以 Order 類別為例。

首先,我們透過反射,取得到訂單類別的所有欄位資訊;然後,使用循環遍歷這些欄位;最後,在循環中,我們放開欄位的存取權限,把欄位 put 到 Document 裡面。

public class Order {
    // ...省略无数字段

    public Document order2Doc(Order order) throws Exception {
        Document doc = new Document();

        // 获取所有字段:通过 getClass() 方法获取 Class 对象,然后获取这个类所有字段
        Field[] fields = order.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 开放字段操作权限
            field.setAccessible(true);
            // 设置值
            doc.put(field.getName(), field.get(order));
        }

        return doc;
    }
}
登入後複製

你可以看到,經過反射改造後,程式碼簡單了許多。一個物件無論有多少個字段,要寫多少 put 操作,只要這幾行程式碼就能搞定。 Java 物件轉換成 MongoDb 的結構,看起來也不那麼麻煩了。

照著這個思路,我們再來改造第二個方法,Document 轉 Java 物件。

public class Order {
    // ...省略无数字段

    public Order doc2Order(Document doc) throws Exception {
        Order order = new Order();

        for (String key : doc.keySet()) {
            // 获取字段
            Field field = order.getClass().getDeclaredField(key);
            // 开放字段操作权限
            field.setAccessible(true);
            // 设置值
            field.set(order, doc.get(key));
        }

        return order;
    }
}
登入後複製

首先,我們使用循環遍歷 Document;在循環中,使用反射來取得對應的字段,再放開字段的存取權限,把 Document 的值設定到物件的字段裡。

到了這兒,我們利用反射,簡化了兩個實體類別的轉換方法,第一版的迭代基本上完成了。剩下的工作,就是複製貼上,把各個類別重新改造一遍。

然而,經過這一版迭代,雖然減少了很多工作,但仍然有很多不合理的地方。

首先,重複程式碼還是很多。每個實體類別都有兩個轉換方法,但這兩個方法的核心邏輯是一樣的,完全沒必要到處複製。

然後,這不是實體類別應該承擔的功能。實體類別只負責短暫保留數據,不負責任何持久化功能。你把資料存到哪裡,該轉換成什麼資料結構,這跟實體類別沒什麼關係。

換句話說,我們還得做第二次迭代。

利用泛型、反射,提取MongoDb 工具類別

簡單來說,泛型是一種風格或範式,你不用一開始就指明具體的參數類型,而是在使用的時候再確定參數型別。

如果把泛型、反射結合在一起,能幫我們減少很多重複程式碼。

我們來看看,該怎麼做第二次迭代?

先從 Java 物件轉 Document 開始。我們先宣告一個泛型方法;然後,透過反射,取得泛型類別的所有欄位信息,再使用迴圈遍歷這些欄位;最後,在迴圈中,把欄位 put 到 Document 裡面。

public class MongoDbUtils {

    // 定义泛型方法:
    // 1. 在返回值前,声明泛型参数 <参数名>;
    // 2. 传入参数时,指定一个泛型参数
    public static <T> Document obj2Doc(T obj) throws Exception {
        Document doc = new Document();

        // 获取所有字段:通过 getClass() 方法获取 Class 对象,然后获取这个类所有字段
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 开放字段操作权限
            field.setAccessible(true);
            // 设置值
            doc.put(field.getName(), field.get(obj));
        }

        return doc;
    }
}
登入後複製

加入泛型後,重複程式碼大量減少了,實體類別不用再單獨寫 2Doc()方法了。使用的時候,只要呼叫 MongoDbUtils.obj2Doc() 就行。

依照同樣的思路,我們繼續來改造第二個方法,Document 轉換 Java 物件。

public class MongoDbUtils {

    // 定义泛型方法:
    // 1. 在返回值前,声明泛型参数 <参数名>;
    // 2. 传入参数必须是 Class,但这个 Class 是泛型参数,不限制类型
    public static <T> T doc2Obj(Document doc, Class<T> clazz) throws Exception {
        // 实例化泛型对象
        T obj = clazz.newInstance();

        for (String key : doc.keySet()) {
            // 获取字段
            Field field = clazz.getDeclaredField(key);
            // 开放字段操作权限
            field.setAccessible(true);
            // 设置值
            field.set(obj, doc.get(key));
        }

        return obj;
    }
}
登入後複製

首先,我们定义实例化一个泛型对象;然后,我们使用循环遍历 Document;最后,在循环中,使用反射获取相应的字段,把 Document 的值设置到泛型对象的字段里。

第二版的迭代就基本完成了。我们在第一版迭代的基础上,加入了泛型,得到了一个工具类 MongoDbUtils,这个工具类得到结果和以前完全一样,你可以看下测试代码。

public static void main(String[] args) throws Exception {
    Order order = new Order();
    order.setId(0L);
    order.setUserId(0L);
    order.setOrderNo("1");
    order.setAmount(new BigDecimal("0"));
    order.setCreateTime("2");
    order.setUpdateTime("3");
    System.out.println("原始数据:" + order);

    Document document = MongoDbUtils.obj2Doc(order);
    System.out.println("转换doc数据:" + document);

    Order order1 = MongoDbUtils.doc2Obj(document, Order.class);
    System.out.println("转换java数据:" + order1);
}

运行结果:
原始数据:Order(id=0, userId=0, orderNo=1, amount=0, createTime=2, updateTime=3)
转换doc数据:Document{{id=0, userId=0, orderNo=1, amount=0, createTime=2, updateTime=3}}
转换java数据:Order(id=0, userId=0, orderNo=1, amount=0, createTime=2, updateTime=3)
登入後複製

这样一来,我们就不用保留实体类上的转换方法了,剩下的工作就是删代码。

MongoDb 和 Java 对象的互相转换就完成了。我们做了两次迭代,第一次迭代利用了反射,把大量手动 set/get 操作给去掉了;第二次迭代在原来的基础上,加入了泛型的应用,又去掉了一堆重复代码。

以上是Java使用反射,把物件轉換成 MongoDb 的結構的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:segmentfault.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板