纠纷奇闻社交美文家庭
投稿投诉
家庭城市
爱好生活
创业男女
能力餐饮
美文职业
心理周易
母婴奇趣
两性技能
社交传统
新闻范文
工作个人
思考社会
作文职场
家居中考
兴趣安全
解密魅力
奇闻笑话
写作笔记
阅读企业
饮食时事
纠纷案例
初中历史
说说童话
乐趣治疗

MybatisPlus框架项目实践总结

8月20日 寒霜坞投稿
  在使用了MybatisPlus框架进行项目重构之后,关于如何更好的利用Mybatisplus。在此做一些总结供大家参考。
  主要总结了以下这几个方面的实践。基础设计BaseEntity逻辑删除自动填充字段代码生成类查询操作Query基类(复用PageQuery)普通QueryLambdaQuery复杂多表查询报表型查询保存操作模型利用JPA保存批量保存数据扩展阻止全表操作动态数据源多租户基础设计BaseEntity
  对于数据库中表中的公共字段我们可以抽取出来做成基类继承。避免表映射的数据库实体类字段太过繁杂。
  例如常用的创建时间、创建者、更新时间、更新者、逻辑删除字段。Entity基类authorvalarchieEqualsAndHashCode(callSupertrue)DatapublicclassBaseEntityTextendsM?extendsModelT{ApiModelProperty(创建者ID)TableField(valuecreatorid,fillFieldFill。INSERT)privateLongcreatorId;ApiModelProperty(创建时间)TableField(valuecreatetime,fillFieldFill。INSERT)privateDatecreateTApiModelProperty(更新者ID)TableField(valueupdaterid,fillFieldFill。UPDATE,updateStrategyFieldStrategy。NOTNULL)privateLongupdaterId;ApiModelProperty(更新时间)TableField(valueupdatetime,fillFieldFill。UPDATE)privateDateupdateTdeleted字段请在数据库中设置为tinyInt并且非null默认值为0ApiModelProperty(删除标志(0代表存在1代表删除))TableField(deleted)TableLogicprivateB}
  通过继承了基类,实体类看起来就简洁了许多。p通知公告表authorvalarchiesince20221002GetterSetterTableName(sysnotice)ApiModel(valueSysNoticeEntity对象,description通知公告表)publicclassSysNoticeEntityextendsBaseEntitySysNoticeEntity{privatestaticfinallongserialVersionUID1L;ApiModelProperty(公告ID)TableId(valuenoticeid,typeIdType。AUTO)privateIntegernoticeId;ApiModelProperty(公告标题)TableField(noticetitle)privateStringnoticeTApiModelProperty(公告类型(1通知2公告))TableField(noticetype)privateIntegernoticeTApiModelProperty(公告内容)TableField(noticecontent)privateStringnoticeCApiModelProperty(公告状态(1正常0关闭))TableField(status)privateIApiModelProperty(备注)TableField(remark)privateSOverridepublicSerializablepkVal(){returnthis。noticeId;}}
  既然抽取出了公共字段,我们可以更进一步将这些公共字段进行自动填值处理。
  MybatisPlus提供了字段自动填充的插件。自动填充字段MybatisPlus允许在插入或者更新的时候自定义设定值authorvalarchieComponentSlf4jpublicclassCustomMetaObjectHandlerimplementsMetaObjectHandler{publicstaticfinalStringCREATETIMEFIELDcreateTpublicstaticfinalStringCREATORIDFIELDcreatorId;publicstaticfinalStringUPDATETIMEFIELDupdateTpublicstaticfinalStringUPDATERIDFIELDupdaterId;OverridepublicvoidinsertFill(MetaObjectmetaObject){if(metaObject。hasSetter(CREATETIMEFIELD)){this。setFieldValByName(CREATETIMEFIELD,newDate(),metaObject);}if(metaObject。hasSetter(CREATORIDFIELD)){this。strictInsertFill(metaObject,CREATORIDFIELD,Long。class,getUserIdSafely());}}OverridepublicvoidupdateFill(MetaObjectmetaObject){if(metaObject。hasSetter(UPDATETIMEFIELD)){this。setFieldValByName(UPDATETIMEFIELD,newDate(),metaObject);}if(metaObject。hasSetter(UPDATERIDFIELD)){this。strictUpdateFill(metaObject,UPDATERIDFIELD,Long。class,getUserIdSafely());}}publicLonggetUserIdSafely(){LonguserItry{LoginUserloginUserAuthenticationUtils。getLoginUser();userIdloginUser。getUserId();}catch(Exceptione){log。info(cannotfinduserincurrentthread。);}returnuserId;}}
  使用自定义填充值时,需要在生成实体的时候加上配置。FieldFill。INSERT和FieldFill。INSERTUPDATEprivatevoidentityConfig(StrategyConfig。Builderbuilder){Entity。BuilderentityBuilderbuilder。entityBuilder();entityBuilder。enableLombok()。addTableFills(newColumn(createtime,FieldFill。INSERT))。addTableFills(newColumn(creatorid,FieldFill。INSERT))。addTableFills(newProperty(updateTime,FieldFill。INSERTUPDATE))。addTableFills(newProperty(updaterId,FieldFill。INSERTUPDATE))IDstrategyAUTO,NONE,INPUT,ASSIGNID,ASSIGNUUID;。idType(IdType。AUTO)。formatFileName(sEntity);if(isExtendsFromBaseEntity){entityBuilder。superClass(BaseEntity。class)。addSuperEntityColumns(creatorid,createtime,creatorname,updaterid,updatetime,updatername,deleted);}entityBuilder。build();}逻辑删除
  数据库一般不进行真实删除操作。但是如果让我们手工处理这些逻辑删除的话,也是非常麻烦。MybatisPlus有提供这样的插件。仅需要在EntityConfig中设置逻辑删除的字段是哪个即可。entityBuilderdeleted的字段设置成tinyint长度为1。logicDeleteColumnName(deleted)。formatFileName(sEntity);代码生成类
  Mybatisplus支持生成entity,mapper,service,controller这四层类。但是笔者认为生成类的时候还是不要直接覆盖原本的类比较好。
  我将生成的类,固定放在一个目录让使用者自己copy类到指定的目录。
  以下是我自己封装的CodeGenerator的代码片段。
  需要填入的字段主要是:作者名包名表名是否需要继承基类(因为不是所有表都需要继承基类)publicstaticvoidmain(String〔〕args){默认读取applicationdevyml中的master数据库配置JSONymlJsonJSONUtil。parse(newYaml()。load(ResourceUtil。getStream(applicationdev。yml)));CodeGeneratorgeneratorCodeGenerator。builder()。databaseUrl(JSONUtil。getByPath(ymlJson,URLPATH)。toString())。username(JSONUtil。getByPath(ymlJson,USERNAMEPATH)。toString())。password(JSONUtil。getByPath(ymlJson,PASSWORDPATH)。toString())。author(valarchie)生成的类放在orm子模块下的targetgeneratedcode目录底下。module(agilebootormtargetgeneratedcode)。parentPackage(com。agileboot)。tableName(sysconfig)决定是否继承基类。isExtendsFromBaseEntity(true)。build();generator。generateCode();}查询操作Query基类
  系统内的查询大部分有共用的逻辑。比如时间范围的查询、排序。我们可以抽取这部分逻辑放在基类。然后把具体查询条件的构造,放到子类去实现。AbstractQueryauthorvalarchieDatapublicabstractclassAbstractQueryT{protectedStringorderByCprotectedStringisAJsonFormat(shapeShape。STRING,patternyyyyMMdd)privateDatebeginTJsonFormat(shapeShape。STRING,patternyyyyMMdd)privateDateendTprivatestaticfinalStringASCprivatestaticfinalStringDESC生成queryconditionsreturnpublicabstractQueryWrapperTtoQueryWrapper();publicvoidaddSortCondition(QueryWrapperTqueryWrapper){if(queryWrapper!null){booleansortDirectionconvertSortDirection();queryWrapper。orderBy(StrUtil。isNotBlank(orderByColumn),sortDirection,StrUtil。toUnderlineCase(orderByColumn));}}publicvoidaddTimeCondition(QueryWrapperTqueryWrapper,StringfieldName){if(queryWrapper!null){queryWrapper。ge(beginTime!null,fieldName,DatePickUtil。getBeginOfTheDay(beginTime))。le(endTime!null,fieldName,DatePickUtil。getEndOfTheDay(endTime));}}publicbooleanconvertSortDirection(){booleanorderDif(StrUtil。isNotEmpty(isAsc)){if(ASC。equals(isAsc)){orderD}elseif(DESC。equals(isAsc)){orderD}}returnorderD}}PageQuery
  分页是非常常见的查询条件,我们可以基于AbstractQuery再做一层封装。authorvalarchieDatapublicabstractclassAbstractPageQueryTextendsAbstractQueryT{publicstaticfinalintMAXPAGENUM200;publicstaticfinalintMAXPAGESIZE500;Max(MAXPAGENUM)protectedIntegerpageNum1;Max(MAXPAGESIZE)protectedIntegerpageSize10;publicPageTtoPage(){returnnewPage(pageNum,pageSize);}}普通Query
  比如我们有个菜单查询列表,我们可以新建一个MenuQuery继承AbstractQuery。然后实现toQueryWrapper方法去构造查询条件。authorvalarchieDatapublicclassMenuQueryextendsAbstractQuerySysMenuEntity{privateStringmenuNprivateBooleanisVprivateIOverridepublicQueryWrapperSysMenuEntitytoQueryWrapper(){QueryWrapperSysMenuEntityqueryWrappernewQueryWrapperSysMenuEntity()。like(StrUtil。isNotEmpty(menuName),menuname,menuName)。eq(isVisible!null,isvisible,isVisible)。eq(status!null,status,status);queryWrapper。orderBy(true,true,Arrays。asList(parentid,ordernum));returnqueryW}}
  如果有另外一个不同的菜单查询列表,查询的参数一样,但是查询条件的构造不一样。我们可以新建一个DifferentMenuQuery类继承MenuQuery类,再覆写toQueryWrapper方法即可。LambdaQuery
  如果在项目中的查询明确是单表操作的话,我们可以使用LambdaQuery来构造查询。LambdaQueryWrapperSysMenuEntitymenuQueryWrappers。lambdaQuery();menuQuery。select(SysMenuEntity::getMenuId);ListSysMenuEntitymenuListmenuService。list(menuQuery);复杂多表查询
  MybatisPlus支持Select注解,遇到简单的多表join查询的话,我们可以直接在代码中写SQL语句。
  以下是Mapper中的实现。{ew。customSqlSegment}会渲染出QueryWrapper类生成的查询条件。根据条件分页查询用户列表parampage页码对象paramqueryWrapper查询对象return用户信息集合信息Select(SELECTu。,d。deptname,d。leadernameFROMsysuseruLEFTJOINsysdeptdONu。deptidd。deptid{ew。customSqlSegment})PageSearchUserDOgetUserList(PageSearchUserDOpage,Param(Constants。WRAPPER)WrapperSearchUserDOqueryWrapper);
  Service层中的实现。OverridepublicPageSearchUserDOgetUserList(AbstractPageQuerySearchUserDOquery){returnbaseMapper。getUserList(query。toPage(),query。toQueryWrapper());}报表型查询
  如果遇到复杂的报表型查询,利用Select注解的话,可能SQL看起来还是非常的复杂。此时推荐使用XML的形式。
  保存操作模型利用JPA保存
  MybatisPlus支持activeRecord特性,我们可以直接在Entity上执行saveupdatedelete等操作。框架会自动帮我们落库。activeRecord需要在EntityConfig配置。entityBuilderoperateentitylikeJPA。。enableActiveRecord()deleted的字段设置成tinyint长度为1IDstrategyAUTO,NONE,INPUT,ASSIGNID,ASSIGNUUID;。idType(IdType。AUTO)。formatFileName(sEntity);
  因为Entity都是生成的,我们不方便将业务逻辑直接放在Entity中。这样会和数据库实体过于耦合。
  推荐新建一个模型类继承XxxxEntity,然后将逻辑填充在模型类中。publicclassDeptModelextendsSysDeptEntity{privateISysDeptServicedeptSpublicDeptModel(ISysDeptServicedeptService){this。deptServicedeptS}publicDeptModel(SysDeptEntityentity,ISysDeptServicedeptService){if(entity!null){如果大数据量的话可以用MapStruct优化BeanUtil。copyProperties(entity,this);}this。deptServicedeptS}publicvoidloadAddCommand(AddDeptCommandaddCommand){this。setParentId(addCommand。getParentId());this。setAncestors(addCommand。getAncestors());this。setDeptName(addCommand。getDeptName());this。setOrderNum(addCommand。getOrderNum());this。setLeaderName(addCommand。getLeaderName());this。setPhone(addCommand。getPhone());this。setEmail(addCommand。getEmail());}publicvoidcheckDeptNameUnique(){if(deptService。isDeptNameDuplicated(getDeptName(),getDeptId(),getParentId())){thrownewApiException(ErrorCode。Business。DEPTNAMEISNOTUNIQUE,getDeptName());}}publicvoidgenerateAncestors(){if(getParentId()0){setAncestors(getParentId()。toString());}SysDeptEntityparentDeptdeptService。getById(getParentId());if(parentDeptnullStatusEnum。DISABLE。equals(BasicEnumUtil。fromValue(StatusEnum。class,parentDept。getStatus()))){thrownewApiException(ErrorCode。Business。DEPTPARENTDEPTNOEXISTORDISABLED);}setAncestors(parentDept。getAncestors(),getParentId());}}
  在应用层我们就可以直接调用模型类来完成逻辑操作。整个代码的语义性非常强。publicvoidaddDept(AddDeptCommandaddCommand){DeptModeldeptModeldeptModelFactory。create();deptModel。loadAddCommand(addCommand);deptModel。checkDeptNameUnique();deptModel。generateAncestors();deptModel。insert();}批量保存数据
  以上是单条数据的落库操作,那么多条数据循环去insert的话,显然不是一个明智之举。
  MybatisPlus提供了批量落库操作。privatebooleansaveMenus(){ListSysRoleMenuEntitylistnewArrayList();if(getMenuIds()!null){for(LongmenuId:getMenuIds()){SysRoleMenuEntityrmnewSysRoleMenuEntity();rm。setRoleId(getRoleId());rm。setMenuId(menuId);list。add(rm);}returnroleMenuService。saveBatch(list);}}按条件更新数据
  JPA的方式有一个弊端就是需要先拿到数据实体类,才能调用save等操作。
  还有一种情况,我们需要按照某些条件去更新数据,而不想先一条条获取数据再Save。
  此时可以使用LambdaUpdate类。LambdaUpdateWrapperSysUserEntityupdateWrappernewLambdaUpdateWrapper();updateWrapper。set(SysUserEntity::getRoleId,null)。eq(SysUserEntity::getUserId,userId);userService。update(updateWrapper);扩展阻止全表操作
  MybatisPlus提供了安全方面的插件,比如阻止全标更新删除的插件。仅需要声明MybatisPlusInterceptorBean,依次添加拦截插件即可。BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptornewMybatisPlusInterceptor();interceptor。addInnerInterceptor(newBlockAttackInnerInterceptor());}动态数据源
  MybatisPlus提供DS注解去动态选择从库还是主库来执行SQL。DS(slave)PreAuthorize(permission。has(system:notice:list))GetMapping(listFromSlave)publicResponseDTOPageDTONoticeDTOlistFromSlave(NoticeQueryquery){PageDTONoticeDTOpageDTOnoticeApplicationService。getNoticeList(query);returnResponseDTO。ok(pageDTO);}
  比如打上了DS(slave)的接口,就会去找slave这个从库进行操作。多租户BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptornewMybatisPlusInterceptor();interceptor。addInnerInterceptor(newTenantLineInnerInterceptor(newTenantLineHandler(){OverridepublicExpressiongetTenantId(){获取租户ID实际应该从用户信息中获取returnnewLongValue(1);}这是default方法,默认返回false表示所有表都需要拼多租户条件OverridepublicbooleanignoreTable(StringtableName){return!sysuser。equalsIgnoreCase(tableName);}}));如果用了分页插件注意先addTenantLineInnerInterceptor再addPaginationInnerInterceptor用了分页插件必须设置MybatisConfigurationuseDeprecatedExecutorfalseinterceptor。addInnerInterceptor(newPaginationInnerInterceptor());}
投诉 评论 转载

冬季要保护好身体的这两个部位,别着凉!否则易损耗阳气初冬季节,北风呼啸,尤其是北方地区,到了冬季以后,寒风一吹,脸蛋都会感到生疼。很多人在生活中会发现,稍微受冷风一吹,自己就容易头疼,感冒,鼻塞。医书典籍里曾这样记载:风者……地球上最冷小镇,当地人认为20很热,零下55学校才会停课认为你在二月的霜冻中挣扎?这个小镇的零度以下的温度算不了什么。这个位于西伯利亚荒野中的定居点的气候通常被认为是地球上最残酷的。Oymyakon镇的居民被誉为最寒冷的……中国未来二十年最有发展潜力的二线城市中国20世纪七十年代末开始的改革开放由沿海向内地传导带动整个中国高速发展,成就中国今日成为仅次于美国的世界第二大经济体。四十多年的改革开放沿海绝大部分省份经济迅速做大做强:广东……MybatisPlus框架项目实践总结在使用了MybatisPlus框架进行项目重构之后,关于如何更好的利用Mybatisplus。在此做一些总结供大家参考。主要总结了以下这几个方面的实践。基础设计BaseE……汤姆斯杯中国队50大胜荷兰挺进八强!石宇奇被雪藏在汤姆斯杯小组赛中国队5:0战胜塔希提之后,今日中国队轻松战胜荷兰队,提前锁定八强席位。本场比赛,考虑到石宇奇前期的比赛体力消耗过大,教练组继续雪藏石宇奇养精蓄锐,以备后面的淘……在这款饥荒厂商开发的卡牌游戏中你既能打架也能打嘴炮随着近年来肉鸽(Roguelike)游戏的大受欢迎,越来越多的开发商也开始肉鸽游戏的开发想着能够分一杯羹。这不,曾凭借着《饥荒》一鸣惊人的开发商Klei也推出了一款这样的游戏《……身体一动就出汗,警惕三种情况夏季天气炎热,很多人一动就出汗,尤其在活动后更是大汗淋漓,比如说在户外走动时,头部、颈部、胸部及后背湿透仿佛刚从水里捞起来一样,出现这样的症状千万不能大意,中医认为一动就出汗属……钾含量是苹果9倍,钙含量是黄豆2倍,多买些晒干囤起来,炖肉特导语:钾含量是苹果的9倍,钙含量是黄豆的2倍,秋天多买些,晒干囤起来,冬天炖肉特香!人间烟火气,最抚凡人心,柴米油盐最是平凡,简简单单却能温暖疲惫的身体,一日三餐四季,承……消息传来,华为挺过来了!美媒这太快了自从被美制裁以来,华为近三年来过得非常不易,从之前的阔步前进突然变得举步维艰,因为遭到了全方位的断供,台积电停止芯片代工、谷歌断供安卓和服务等等。然而,华为并没有像日本东……爱自己,就是不跟自己过不去王尔德说:爱自己,是终身浪漫的开始。那么,怎样才算爱自己?是舍得给自己花钱,还是不让自己受委屈?是给自己办个健身卡,还是做让自己开心的事?曾见过很多姑娘,对着……扳大罾的记忆,那时候河里有很多的鱼江苏乃水乡,以河流众多闻名全国。我老家苏北也是如此,大河小沟到处都是,那时出行,不是过桥就是坐船。有河有水就有鱼,有鱼就可以捕。捕鱼的方式很多,多到都无法列举,场面比较宏……孙兴慜发力!从11!前亚洲冠军出现在望,国足出线基本破灭亚洲区世界杯预选赛中太极虎在前4轮中,进球虽然不多,只打进区区四粒进球,但凭借他们强悍的防守,积分抢到了8分,平均每场打进1球,他们排在伊朗之后,排名小组第二,出线形势基本牢牢……
过敏性鼻炎高发季,3个特殊原因2个中医小贴士都清楚了昆明10月大男婴打疫苗后身亡,鉴定结果出炉疫苗惹的祸,你怎么天府新区楼市硬着陆,业主回到田里种红薯,中介也回老家农民夏天在农村田间作业,被大过山蜂咬伤,过山峰毒性那么强,是宝宝被蚊子咬了,有红包,宝宝止不住用手挠,有什么办法快速消肿养生之道长寿秘诀macpro安装maven海口一的哥身患癫痫,单位却拒绝解除合同,的哥已带病开车一个多卫健委发文重点监控20种药品,要求西医经过1年培训才能开中成为什么睡完午觉后感觉人有点抑郁,而且是醒的越晚越抑郁的那种?助听器耳模是怎么制作的?小行星龙宫可能接近过太阳,科学家也感到意外
关于大学英语四六级考试改革背景下如何加强词汇教学2018正能量的一句话签名致自己减少教育行政干预,切实为校长减压我的妈妈作文800字12月20日李佳琦薇娅烈儿直播预告【歌词】。2010Levis李维斯店铺旁边洗头房店铺音乐音质 宝宝肥胖有哪些原因和危害《温暖,倾城》苹果怎么查定位手机位置(如何找到iphone手机位置)人事个人工作总结老公拥有一颗骚动的心我该怎么办字节跳动布局电商野心拼多多SheinFacebook?

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找美丽时装彩妆资讯历史明星乐活安卓数码常识驾车健康苹果问答网络发型电视车载室内电影游戏科学音乐整形