当前位置:主页 > 聚焦 >

Java使用反射,把对象转换成 MongoDb 的结构

时间:2020-12-18 22:56:20

  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 操作给去掉了;第二次迭代在原来的基础上,加入了泛型的应用,又去掉了一堆重复代码。

热点推荐
1 鲸鱼地址0x527提取37.06万枚LINK,当前持有

消息,链上监测数据显示,鲸鱼地址0x527从某平台提取了370,631枚LINK,价值约348万美元,目前该...

2 埃及与巴基斯坦外长通话讨论美伊重回外

消息,埃及外长阿卜杜勒阿提于24日与巴基斯坦副总理兼外交部长达尔通电话,双方就推动美国...

3 比特币ETF总净流入达1447万美元

消息,4月24日比特币ETF总净流入为1447万美元。其中,黑石的IBIT净流入2290万美元,摩根士丹利...

4 鲸鱼0x2d2d过去7小时再购入200枚ETH,持有

鲸鱼地址0x2d2d在过去7小时内再次花费200枚ETH购买了17.2亿枚$ASTEROID。目前该地址持有8.38亿枚$...

5 Balancer黑客将14300枚ETH换成419.3枚BTC,价值

Balancer黑客从昨天开始通过Thorchain将14,300枚ETH换成419.3枚BTC,按当前价格计算约合3251万美元。目...

6 波兰加密交易所ZondaCrypto濒临破产,99%比

加密交易所ZondaCrypto的首席执行官Przemysław Kral已前往以色列,因其拥有以色列国籍,可能难以...

7 量子计算机在比特币相关测试中破解ECC密

消息,研究人员使用公共量子硬件成功破解了一个15位的椭圆曲线密钥,赢得了项目Eleven的1 ...

8 Bitget将于4月30日下架22个现货交易对

消息,Bitget宣布将于2026年4月30日上午10:00下架22个现货交易对,此举是基于定期审查的结果。用...

9 Bridgetower采用Chainlink基础设施,完成110亿

Bridgetower Capital已正式采用Chainlink的完整基础设施栈,将110亿美元的证券代币化,涉及Dom X亚利...

10 Polymarket:Kevin Warsh被确认为美联储主席概

消息,预测市场显示,当前关于美联储主席确认的热门事件中,Kevin Warsh的确认概率为98.7%。截...

成都来彰科技 蜀ICP备2025134723号-1

资讯来源互联网,如有版权问题请联系管理员删除。