在一次开发过程中,我发现后端程的工作其实很快就能完成,而前端的进度几乎就决定了开发的进度。所以,我就觉得后端的开发人员在返回数据的时候尽量让前端拿到数据可以直接使用,而不要让前端再转一次。而字典数据就是典型的例子,比如合同的状态在签订中、执行中、执行完成、已废止,员工的职位是普通员工、部门经理、总经理等等,这些数据在数据库中储存的是数字,但是在页面显示的时候需要转成汉字。
后端人员想要将这些数据转成数字其实也很简单,比如重写实体类的setter方法,在set相应的属性的时候就直接进行判断然后转成我们想要的数据就行了,但如果系统大一点的话就不太好管理了,比如员工的职位想要增加一个,但是有多个视图用到了员工职位,那么改的setter方法的时候就需要改多个类。而且如果遇到既需要原本的数据,又需要转换后的数据的情况就很难处理,种种不便我就不再赘述了,这里介绍一个通过自定义注解、反射和Spring AOP实现字典查询的方法来解决这一问题。
首先看看效果:
Student实体类
student实体类中在teacherId、sex和type属性上加了一个@Dict注解。
StudentController
在getStudent和StudentById上加了@QueryDict注解。
请求getStudent方法后得到的结果
可以看到,我们并没用重写任何一个setter方法,但是在最终的请求结果中我们却可以看到多了sex_dictText,type_dictText,teacherId_dicText这三个数据,而且这三个数据看起来就像是原本实体类里面的属性一样。下面就来讲讲如何实现这样的效果。
首先看一下自定义注解:
@Dict注解
dicText和dictTable有默认值,使用的时候可以不传,但传与不传会让dicCode这个属性的作用不同,这个后面再说。
@QueryDict注解
这个注解没有属性,它只是作为一个进行字典查询的标识。
接下来就是使用Spring AOP和反射来进行实现了:
第一步,定义切点
定义切点这里就用到了第二个注解@QueryDict,当使用了@QueryDict这个注解之后,就会进入这个切面。可以看到这里是将目标方法的返回值进行了更改。
下面就来看看是如何更改的:
将result分成了两类进行处理
要进行字典查询的情况只有两种,一种就是进行分页查询的时候,一种就是查询具体哪条数据的时候。用过instanceof来判断是否是IPage类型就可以判断出是否是分页查询了(因为这里是通过mybatis-plus实现查询的),如果是分页查询,那么就有进行迭代,然后再调用eachField来进行下一步判断和更改操作。
不过这里需要先插入一段我的Result类:
Result返回结果类
只需要看它的属性就够了。然后就是eachField方法
eachField方法
这个方法其实就是重新定义了一个json,然后将原先的result返回数据直接添加进去,然后再获取返回数据的每个属性,如果这个属性添加了@Dict注解,就使用translateDicValue来进行字典翻译(可以通过查询数据库来实现,也可以进行通过查询枚举来实现。),然后将翻译后的数据添加进前面定义的json就可以了。上面代码中,oConverUtils.getAllFileds是获取返回数据的所有属性,里面的具体实现是通过getDeclaredFields方法实现的。field.getName()+CommonConstant.DICT_TEXT_SUFFIX是为了给翻译后的数据定义一个键名,就是前面测试结果看到的xxx_dictText。
translateDictValue翻译字典方法
这里调用了dictService.QueryDict方法:
QueryDict方法
可以看到,dictTable和dictText为空字符串和不为空字符串的时候调用的sql是不一样的,不为空字符串的时候就代表着要去其他基本表查。
比如teacherId这个属性添加了@Dict(dictTable ="teacher",dicText ="teacher.teacher_name",dicCode ="id")就表示要去teacher表查teacher_name这个字段,查询条件是teacherId属性的值等于teacher表中的id。
如果dictText为空字符串的话就要去专门的字典表中查。
最后来看一下sql语句
字典查询的sql语句
这样通过自定义注解、反射和Spring AOP实现字典查询的方法就差不多交代清楚了。