Java POJO映射

背景

考虑以下典型的应用场景:在页面上点击按钮,向后台发送一个请求,后台查询数据库,并对数据做处理,最终返回一些数据,展示在页面上。

通常情况下,应用在从数据库查询数据时,会把获取到的结果集(resultset)映射为POJO的List。例如,把 select * from t1 的resultset映射为 List<MyPojo> 对象。接下来,可能对该List进行各种操作,比如计算(calculation),过滤(filter),聚集(aggregation),排序(sorting),截取(limit&offset),转换(transformation)。最终,把List对象以JSON格式发送给前端。

例如,学生成绩的table如下:

NameScore_ChineseScore_EnglishScore_Math
Tom609093
Jerry768271
John8482100

评选奖学金的条件为:

  1. 每科成绩均不小于80分;
  2. 总成绩不小于260分;

要查询符合奖学金条件的学生,当然可以直接用SQL查询:

select Name, Score_Chinese, Score_English, Score_Math, Score_Chinese + Score_English + Score_Math as Score_Total
from t1
where Score_Chinese >= 80 and Score_English >= 80 and Score_Math >= 80
    and Score_Chinese + Score_English + Score_Math >= 260
order by Score_Chinese + Score_English + Score_Math desc

这种做法,效率显然是非常高的。当然,此时需要POJO里有 Score_Total 属性,以对应resultset里的 Score_Total 字段。

但是,假如数据不是存储在数据库里,比如是存在memory cache里的,而cache只是单纯的存取数据,没有任何计算功能,相当于针对数据库的查询 select * from t1 ,这种情况下就只能在Java代码逻辑里进行运算了。

对象映射

假定source POJO有100个字段,target POJO有其中的50个字段。

方法1

通过匹配方法名。具体来说,就是找到targetPOJO的所有 setXxx() 方法,对其中每个方法,查找targetPOJO中对应的 setXxx() 方法,然后通过 targetPOJO.setXxx(sourcePOJO.getXxx()) 来给sourcePOJO的属性赋值。

例如:targetPOJO有个 setId() 方法,则sourcePOJO中一定有一个 getId() 方法,所以可以通过 targetPOJO.setId(sourcePOJO.getId()) 来给targetPOJO的 id 属性赋值。

那么,如何才能找到targetPOJO的 setXxx() 方法,以及sourcePOJO对应的 getXXX() 方法呢?我们在编译期无法得知targetPOJO都有哪些方法。因此,只能考虑使用反射(Reflection)了。具体步骤如下:

1)获取targetClass的所有方法,得到一个集合(Array或者List);
2) 对集合中的每一项(对应targetClass的一个方法),假定为 tm ,通过 getName() 方法获取其命名;
3) 如果该方法命名不是以 set 开头,则略过,否则获取 set 之后的字符串,假定是 Xxx
4) 获取sourceClass的所有方法,得到一个集合(Array或者List);
5) 遍历集合,查找命名为 getXxx 的方法,假定为 sm
6) tm.invoke(TargetPOJO, sm.invoke(sourcePOJO))

方法2

既然已经用到反射,那就可以直接访问所有成员变量(即使是private的),而无需通过setter/getter来访问了。

这么做有几点好处:

  1. POJO无需settter/getter;
  2. sourceClass支持 record ,而无需传统POJO。注意:record的getter不是 getXxx() ,而是 xxx() 。另外,record的成员变量都是final的,无法给成员变量赋值,也没有setter,出于这个考虑,targetClass还是使用了传统POJO;
  3. 在性能上,直接获取成员变量的值,显然比通过调用getter方法来获取值要好。同理,直接给成员变量赋值,显然比通过调用setter方法来赋值要好;

方法3

前面的例子,都是通过反射来创建每个targetClass对象,再把对象添加到targetList里,如此循环。如果先创建好整个targetList,效果会不会更好呢?

代码

https://github.com/dukeding/testProject0312

结果

Iteration 0, Method 1: Max = OptionalLong[879], Min = OptionalLong[427], Avg = OptionalDouble[573.3]
Iteration 0, Method 2: Max = OptionalLong[622], Min = OptionalLong[392], Avg = OptionalDouble[519.4]
Iteration 0, Method 3: Max = OptionalLong[617], Min = OptionalLong[384], Avg = OptionalDouble[497.7]
Iteration 1, Method 1: Max = OptionalLong[628], Min = OptionalLong[435], Avg = OptionalDouble[505.0]
Iteration 1, Method 2: Max = OptionalLong[689], Min = OptionalLong[377], Avg = OptionalDouble[498.0]
Iteration 1, Method 3: Max = OptionalLong[609], Min = OptionalLong[389], Avg = OptionalDouble[506.8]
Iteration 2, Method 1: Max = OptionalLong[624], Min = OptionalLong[447], Avg = OptionalDouble[536.5]
Iteration 2, Method 2: Max = OptionalLong[582], Min = OptionalLong[379], Avg = OptionalDouble[483.9]
Iteration 2, Method 3: Max = OptionalLong[692], Min = OptionalLong[380], Avg = OptionalDouble[502.0]
Iteration 3, Method 1: Max = OptionalLong[599], Min = OptionalLong[470], Avg = OptionalDouble[539.0]
Iteration 3, Method 2: Max = OptionalLong[693], Min = OptionalLong[403], Avg = OptionalDouble[524.0]
Iteration 3, Method 3: Max = OptionalLong[676], Min = OptionalLong[389], Avg = OptionalDouble[514.1]
Iteration 4, Method 1: Max = OptionalLong[637], Min = OptionalLong[492], Avg = OptionalDouble[543.1]
Iteration 4, Method 2: Max = OptionalLong[631], Min = OptionalLong[408], Avg = OptionalDouble[506.7]
Iteration 4, Method 3: Max = OptionalLong[707], Min = OptionalLong[402], Avg = OptionalDouble[538.1]

从运行结果来看,方法1的性能相对差一些,方法2和方法3相对好一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值