前天犯了一个不大不小的错误,由于上层映射的属性没有删除,直接修改了SQL,造成数据映射发生错位,进而数据异常。觉得这类错误,当时是想到了的,但是还是错误发生了。进而思考了一下,原来Java也有类似的东西,ORM映射。那个时候并没有出过类似的问题,后来看了一下客户端读取csv的时候,有了进一步的思考。OO语言的映射方式和注意点和C++映射方式的注意点是有区分的。并且变向也体现出来OO语言的便利性。
假如让我用代码实现一个映射关系的化,我第一个想到的是反射机制,反射提供了很方便的反序列化,反射是和序列化相关的。都说反射消耗效率,那么看我下面用C#写的一个测试代码,10W次通过反射创建实体类和通过普通的直接创建的时间对比。
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Threading.Tasks;
6: using System.Reflection;
7:
8: namespace test
9: {
10: class Program
11: {
12: static void Main(string[] args)
13: {
14: Console.Write("Reflection begin:{0}s,{1}ms\n", DateTime.Now.Second,DateTime.Now.Millisecond);
15: for (int i = 0; i < 1000*10*10; i++)
16: {
17: Test ect = (Test)Assembly.GetExecutingAssembly().CreateInstance("test.Test");
18: }
19: DateTime stopTime = DateTime.Now;
20: Console.Write("Reflection end:{0}s,{0}ms\n", DateTime.Now.Second, DateTime.Now.Millisecond);
21:
22: Console.Write("Normal begin:{0}s,{0}ms\n", DateTime.Now.Second, DateTime.Now.Millisecond);
23: for (int i = 0; i < 1000 * 10 * 10; i++)
24: {
25: Test ect = (Test)Assembly.GetExecutingAssembly().CreateInstance("test.Test");
26: }
27: Console.Write("Normal end:{0}s,{0}ms", DateTime.Now.Second, DateTime.Now.Millisecond);
28: Console.ReadKey();
29: }
30: }
31:
32: public class Test
33: {
34: public int test_0 { get; set; }
35: public int test_1 { get; set; }
36: public int test_2 { get; set; }
37: public int test_3 { get; set; }
38: public int test_4 { get; set; }
39: public int test_5 { get; set; }
40: public int test_6 { get; set; }
41: public int test_7 { get; set; }
42: public int test_8 { get; set; }
43: public int test_9 { get; set; }
44: public int test_10 { get; set; }
45: public int test_11 { get; set; }
46: public int test_12 { get; set; }
47: public int test_13 { get; set; }
48: public int test_14 { get; set; }
49: }
50: }
输出如下:
反射在于效率消耗上还是挺大的,看起来倒是很恐怖,但是少写了很多的东西,并且策划表也很难到10W这个量级。更何况一个SQL查询出来更是不可能一下子这么多数据。基本差别还是在毫秒以内。
如果客户端采用反射机制来读取csv表,那么程序这边只需要保证变量名和表里面的填写的头名字一致就可以了,通过反射自动创建实例和方法,只要保证类名和csv对应即可。如果出现错误也是编译时错误,并且异常控制而言更精准。不会造成运行时才会出错的情况且方便只需要修改一处就可以了,不需要两头名字还需要对齐之类的操作,当列多的时候策划频繁更改表的时候,能够保证程序这里第一时间能够反映出来,并且异常提示精准。
然而C++就没这么好命了,C++没有反射。现在仔细一看csv表解析的映射和DBServer处理的映射手法是一致的。csv还好是采用字符串做的一一匹配不会出现错位的情况。然而DB数据的映射并没有根据列名做一个精准对应,而是根据列数组下标形式做的结果集对应。编译时是不过检查的!运行时不看到也不见得能知道错!
C++很类似于C语言,C++更加方便与内存映射。这就很直接牵扯到一个序列化,反序列化的问题。我们只能定义序列化和反序列化方法来自己做处理。当然也可以通过json,xml,protobuf来做序列化和反序列化的操作来减少运行时错误。
补充一点:对于异常的处理,断言,错误告警等处理,如果在效率考量的前提下尽量还是控制在编译时,进而再是启动时运行,进而再是操作时的运行!