eladmin JPA转Mybatis与后端框架迭代更新历程
本文会持续记录博主对eladmin框架的完善以及转化mybatis plus的历程
[scode type="share"]2020年8月15日受腾飞学长委托,要为小组搭建一个之后要一直使用的后端管理系统框架,并以el admin为主,进行删改和重构,将原有的持久层框架jpa修改为mybatis plus[/scode]
[scode type="blue"]2020年8月16日正式开始转化[/scode]
- 首先一开始是熟悉eladmin的后端开发手册,其次是了解eladmin的源码。
- 一开始由于eladmin本身功能比较丰富,也比较“重”,可能对于部分小项目用不了那么多完备的功能,首先是对原有的eladmin进行删模块,将原本的5个核心模块删为了两个,一个是system,另一个是权限控制secuirty,并删去了很多无需的表和方法,最终测试可以运行。
[scode type="blue"]2020年8月17日比葫芦画瓢[/scode]
- 开始对着原eladmin开始比葫芦画瓢,自己一点点分模块开始搭建,一开始搭建还是比较慢的,要改pom,注释等等;直接复制jar还要改import等等,还是比较慢的。
- 晚上继续搭建后端系统,主要是处理登录这一块的内容,因为接口转化还过早,所以现在先把security这块转了,但是我发现这一块也很麻烦,弄了一晚上自己的还没等上去,主要因为security里就用到了jpa查询,而且还不少。所以转化很慢。
[scode type="blue"]2020年8月18日——jpa+mp计划[/scode]
- 上午继续针对转化security这一块开动,仿照他的查询我也写了不少方法,但是由于eladmin没有使用bo,所以实体类里直接有关联属性,但是我查询的时候就比较模糊,不知道是全查还是单个查,导致数据总是为null。
- 还有mapStruct的转化,用于转化dto和entity的(没有用于转化bo的)。
还有各种jackson和缓存的报错
org.springframework.security.authentication.InternalAuthenticationServiceException: com.alibaba.fastjson.JSONObject cannot be cast to me.zhengjie.modules.system.service.dto.UserDto // No cache could be resolved for...
- 所以这一块还需要大量的时间去研究才行。
- 因为项目可能就要用到这个后台管理系统,所以我的转化只能先停掉,采用他的原jpa加上我们的mp做这次后端。
- 所以我在他的代码基础上引入了mp,并测试了新接口,没问题。
- 但是发现验证码为0时会报错误,经发现是esay-capture生成结果为0的验证码时存入redis的时0.0,所以会和0匹配错误。而eladmin这边也没处理这个问题。明天我会修改这两个问题并提两个pr。
[scode type="blue"]2020年8月19日——给eladmin提PR[/scode]
上午主要修改昨天发现的验证码出现浮点数的问题,主要在/auth/code接口中:
//当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型 String captchaValue = captcha.text(); if (captcha.getCharType() - 1 == LoginCodeEnum.arithmetic.ordinal() & captchaValue.contains(".")) { captchaValue = captchaValue.split("\\.")[0]; } // 保存 redisUtils.set(uuid, captchaValue, loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES);
- 也是学了学如何在github上提pr,相对于这个是修复bug的,所以要先提issue,在提pr关联这个issue。
- 上午针对其修复的方式其实治标不治本,真正意义上还要修改easy-captcha本身的代码才行。所以我又把easy-captcha的代码拉了下来,研究产生浮点数的根本原因。
最终定位在这一句:
ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("javascript"); try { chars = String.valueOf(engine.eval(sb.toString().replaceAll("x", "*"))); } catch (ScriptException e) { e.printStackTrace(); }
- 目前的解决方法还是使用split分割。
- 之后继续转化security,目标是多对多查询,在我查询user的时候,把其部门也查出来,还有他的多个角色和多个工作。但是这个sql语句执行之后一直查询失败,目前还在找原因。
[scode type="blue"]2020年8月21日——理解jpa_mp中的security流程[/scode]
- 上午继续对jpa_mp分支做调整。删除一些无用类,修改一些注释,补添了微信授权接口。
- 下午开始理解jpa_mp中的security流程,了解整个项目中各个类的作用。
- 之后对后端框架进行打包,发现打包过程中就出现了不少错误,得先把原先的target都删掉,再对核心模块进行打包。
- 之后在cmd上运行时发现乱码,原因是cmd默认系统编码是gbk,需要先修改系统的编码。而且cmd不支持彩色日志。
[scode type="blue"]2020年8月22日——master完成关联查询[/scode]
- 上午继续推荐mybatis转化——master分支的推进。
- 上午还是在处理查询用户的超难关联查询,在查询用户的同时把多个job和多个role查出来,其中role里还包含了多个menu,所以整体就很麻烦。
- 而且还有id封装为null的bug,最后各种调试才解决。并且抛弃了bo,直接把关联实体写在了entity里,使用@TableField(exist = false)来标明。
有关与中间表的查询,关联类的查询需要自己再写sql语句:
@Select("SELECT * FROM user u WHERE u.username = '${name}'") @Results({ @Result(column = "user_id", property = "id"), @Result(column = "dept_id", property = "deptId"), @Result(column = "dept_id", property = "dept", one = @One(select = "marchsoft.modules.api.mapper.DeptMapper.selectById" , fetchType = FetchType.EAGER)), @Result(column = "user_id", property = "roles", many = @Many(select = "marchsoft.modules.api.mapper.RoleMapper.findWithMenuByUserId", fetchType = FetchType.EAGER)), @Result(column = "user_id", property = "jobs", many = @Many(select = "marchsoft.modules.api.mapper.JobMapper.findByUserId", fetchType = FetchType.EAGER)) }) User findByName(String name);
@Select("SELECT r.* FROM role r, users_roles ur WHERE r.role_id = ur.role_id AND ur.user_id = ${id}") @Results({ @Result(column = "role_id", property = "id"), @Result(column = "role_id", property = "menus", many = @Many(select = "marchsoft.modules.api.mapper.MenuMapper.findByRoleId", fetchType = FetchType.EAGER)) }) Set<Role> findWithMenuByUserId(Long id);
- mybatis关联查询时column的值不能加表名!
- 下午继续推进登录流程的转化,完善其他的方法。
晚上继续完善登录流程,处理了一个大问题,首先是redis缓存,总是报错,之后发现类上少加了一个注解,方法上也要加注解:
@CacheConfig(cacheNames = "user") + @Cacheable(key = "'username:' + #p0")
- 还有一个问题是缓存和数据库不统一,需要及时清理缓存,从新查数据库。
[scode type="blue"]2020年8月23日——完成主页面显示[/scode]
- 上午继续完善登录流程,最终是通过了前端的验证,但是想要显示主页,还要完善Menu的build接口。主要是查询菜单的。
- 之后一直完善菜单的接口。也是各种的多表查询和封装,最后完成,可以成功登录并显示首页了。
- 下午是对第一版转化成功(登录)做一些完善,加一些注释。
[scode type="blue"]2020年8月24日——验证权限管理[/scode]
- 上午开始对各个接口进行转化,第一个是部门接口,先转化了download接口,但是第二个就难到我了。第二个接口是queryAll,其中用到了他们的Query、QueryHelp、DataPermision,这些是和JPA强耦合的,所以转化起来会慢一些。
- 之后学长给我之前的jpa_mp提了一些建议,我开始修改,主要是让其他同学上手容易。之后一直在完善readme.md文档。
- 下午学长让我验证一下jpa_mp权限这一块,包括新增角色、用户,分配权限等。研究了后台管理系统如何给角色分配菜单等等。
- 自己在前端新建接口的时候发现权限拦截不了,最后才发现原来一直运行的是之前打的jar包。
- 最后验证权限分配拦截没有问题。
[scode type="blue"]2020年8月25日——mp分支转化dept接口[/scode]
- 上午继续转化dept接口,继续研究其中最难的查询方法,但是看了一段时间后还是无果,转向其他接口了。
- 其中包含基础的增删改接口。
- 下午继续转化dept接口,这些接口相对来说简单一些,所以其他接口基本上转换完毕。
- 同时之前的jpa_mp分支又出现了一些bug,并修复。
[scode type="blue"]2020年8月26日——mp分支转化menu接口[/scode]
- 上午主要就是处理menu的接口的转化,当然是排除查询接口。
- 下午把menu接口转化完毕。
- 晚上在处理角色的缓存bug,eladmin是这么设计的,如果该角色是管理员,则缓存里只用存管理员一个角色即可,默认拥有所有权限。
- 同时了解到springboot和redis整合的使用:@Cacheable + redis +#p0
[scode type="blue"]2020年8月27日——jpa_mp添加新模块tools[/scode]
- 之后开始转化user接口。也是基本上没有太大的难度。
- jpa_mp分支新加api_tools模块,用于文件上传和微信处理。
[scode type="blue"]2020年8月28日——mp分支转化剩余接口[/scode]
上午先处理昨天写了user接口之后,项目启动失败的问题。报错原因是userServiceImpl动态代理注入失败的问题:
because it is a JDK dynamic proxy that implements
具体愿意不知道为什么,反正我一注入UserServiceImpl就报这个错误,最后的解决方法是在springboot启动类的@EnableTransactionManagement加上参数:(改为GGlib基于类代理,而不是基于接口代理,在yml配置文件中设置aop.proxy无效)
@EnableTransactionManagement(proxyTargetClass = true)
- 之后添加api_tools模块,初始化文件上传接口合微信第三方接口。
- 下午继续转换job和role接口。基本上没有什么难的地方。
[scode type="blue"]2020年8月29日——修改接口bug[/scode]
- 修改user和role接口的获取不到数据的bug。(数据库查非空数据使用的是 is null 而并非 = null)。
[scode type="blue"]2020年8月30日——重写文件上传[/scode]
- 根据昨天讨论的文件上传的功能,发现原有的eladmin的功能不能实现本次业务需求,而且很多冗余方法,只得从新写,就开始看eladmin的文件上传源码再结合之前club的上传源码。
- 下午开始写文件上传模块,并删除之前的文件上传模块。重构数据库,用mp实现上传业务。
- 主要是两个接口,一个是单文件上传,一个是文件下载。
[scode type="green"]有关jpa_mp分支的更新就到8月底了,剩下的优化和修复都在新院统战项目中进行了,本来这个分支就是过渡分支。纯mp分支由于在小组内部仓库,这里不做展示,后期都整合到了SMPE框架里。[/scode]
[scode type="green"]之后jpa_mp版框架成功应用于新院统战、平安科院、科研管理系统三个项目中,至此,小组框架第一版的搭建到此结束[/scode]
[scode type="yellow"]2020年11月12日——讨论第一版框架的问题,更新第二版[/scode]
讨论内容总结:
el-admin通用环境(marchsoft_api)问题
- mybatis @one、@many是否使用?
- mybatis的缓存和mp的分页插件有问题,mapper层加注解pageSize等信息有问题,service没问题。
- 系统用户查询相关比较复杂?
- 请求返回格式不统一
- 第三方依赖引入比较乱(重复引入,最小引入)
- 第三方以子库形式引入我们的主仓库,第三方维护文档
- 微信小程序和公众号分开
- 第三方服务不应该以接口的形式出现
- 框架缺少日志处理
平安科院后续:
星星:
- 缓存机制不清晰
a. 存的什么?
b. 什么时候存的? - 持久层选择(jpa,mybatis)
- 部分功能使用复杂
- 直接修改数据库,有redis缓存,数据会存在问题(没走缓存)
- token签名验证太死了,不好做扩展
晓珊问题:
- 注释
- 框架使用手册,对于新手来说很难入手
- 没有搭框架,学到的东西很少
讨论问题结果:
- mybatis @one、@many是否使用?
讨论结果:不建议使用
● join查询:查询时候可以使用join连接查询,支持最多3张表join查询
● 业务组装:将多张表的信息返回到业务层,然后利用for循环语句组装数据,效率比@One,@many要快
● 利用缓存:可以使用@one和@many,第一次查询时效率慢些,但对接下来查询,会直接从缓存中查询,不用走数据库 - mybatis的缓存和mp的分页插件有问题,mapper层加注解pageSize等信息有问题,service没问题。
讨论结果:
● 不用mybatis-plus 的分页插件,找其他插件。
● 小组自己开发分页插件
调研结论: - 分页插件没有问题
- 默认业务mapper类加二级缓存,指定Radis
- 默认业务service不再添加Radis缓存
- 对应mapper开二级缓存,允许使用@one/@many
- 系统用户查询相关比较复杂?
讨论结果:暂时未定 - 请求返回格式不统一?
讨论结果:
● 返回的状态码用枚举,返回值不定死 - 第三方依赖引入比较乱(重复引入,最小引入)
讨论结果: - 不随意引用第三方模块插件、引用前需大家确人 (note by lqc)
- 去掉现有重复引用,保留安全的
- 放置位置
a. 在common文件下放置与业务无关的工具类,通用方法封装
b. 在tools文件下放置与业务相关的工具类,如:短信工具类,封装接口工具类(hik API) - 需要有使用说明手册
- 第三方以子库形式引入我们的主仓库,第三方维护文档
讨论结果: - 第三方库需要维护使用文档
- 我们的第三方围绕着(基于其项目结构)eladmin开发,服务以模块形式存在
- 微信公众号模块
a. 消息队列分离到项目业务,公众号模块只对外提供使用接口 - 三方服务不应该以接口的形式出现
讨论结果: - 对外需不需要提供服务接口,如果不需要,封装成业务工具类放在tools下
- 微信小程序和公众号分开
造成配置信息混乱的解决方法
a. 在配置属性前加 前缀wx: #微信公众号 mp: #微信小程序 miniapp:
- 框架缺少日志处理
框架日志侧重对敏感业务日志进行收集。
讨论结果: - 日志位置
a. 失败出(必)
b. 敏感操作,增删改 - 信息(内容)
a. 不输出无用信息,主(谁)谓(动词)宾(对谁)状补(干了什么,怎么样了)
b. 输出信息中包含自己的唯一信息(邮箱、名字缩写如lqc)
c. 本地调试可以打印SQL结果,上线不允许打印,但可打印执行的SQL语句 - 格式待定……
等级
a. 框架中常用日志等级(从低到高): Debug->Info->warning->Error
b. 各日志等级使用i. Debug: 在本地开发中使用,可打印任何打印信息 ii. Info:打印业务日志 iii. Error:报错,业务预期/非预期错误,站在业务角度:需要关注的程序异常都用此等级打印
c.
- 缓存机制不清晰(存的什么?什么时候存的?)
讨论结果: - 存放缓存的地方需说明,维护一个文档
- 缓存集中放在一处(redis)
- 修改数据库要在群中通知一下大家, 这个规定放在维护文档中
- 使框架业务不依赖与redis,部分可依赖(登录、排行榜)
问题遗留: - redis和springboot环境中有两份缓存,找出两份的原因(内存机制中,mybatis,redis)
- 持久层选择(jpa,mybatis)
讨论结果:改成完全 mybatis plus - 部分功能使用复杂
由于jpa和mybatis共存原因引起,后期改成mybatis plus可缓解。 - 直接修改数据库,有redis缓存,数据会存在问题(没走缓存)
同问题 10 - token签名验证太死了,不好做扩展
token只提供了读取方法,未提供设置方法,想对其修改比较麻烦,如果有个设置的方法会更好(更方便)。 - 注释
讨论结果: - Controller层有swagger注释的可不加注释
- 类、接口、方法至少需要注明:作者、时间、日期,描述功能
- 在必要处,如分支等地方
- 在改别人代码时,在注释中加入自己修改的名字和修改日期
- 框架使用手册,对于新手来说很难入手
讨论结果:基本的文档是必须的 - 没有搭框架,学到的东西很少
尝试着去搭一下,先会用然后再搭建;
多多向下传承,搭建过整合过的带带新人; - 项目单元测试
之前没有进行,之后需要规范。
单元测试目前统一是测试业务层 - 内置角色
系统中固定不能删除的角色 - 数据加密
如果后端负责全部的加密解密工作可能不好做,把解密放在前端会好点,和前台约定 - 测试问题
- 需要在开发时使用相应的测试号进行测试
- 没有单元测试(同问题18)
- 代码规范问题
a. 对报错的警告也要处理
b. 推荐使用阿里巴巴java插件对代码检查 - 第二版框架整合安排
- 把一版框架改成mybatis plus(花费1~2天),近期有项目用这个环境
- 第二版改成mybatis plus的,并解决以上出现的问题
- 框架搭建完毕,经过测试没有问题,把咱们的环境放到github上
[scode type="red"]这次讨论之后便是确定了框架第二版的更新方向,但是由于up主要于12月26日参加2021考研,所以这次便没有参与第二版的开发,交接给组内另一位大佬管理,并正式命名为SMPE框架[/scode]
[scode type="share"]博主考研结束后,于2021年1月10日继续参与后端框架的更新中,主要处理缓存问题[/scode]
[scode type="blue"]2021年1月10日,讨论SMPE框架目前遗留的问题[/scode]
- 目前框架仍然遗留的大问题就是缓存问题,晚上和星星讨论的结果就是 重新实现@One与@Many方法,在开发效率和之前保持一致的同时又可以使用springboot的缓存。
[scode type="blue"]2021年1月11日~12日,开发新注解@Query[/scode]
[scode type="blue"]2021年1月13日~14日,讨论缓存维护并整合SMPE[/scode]