专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

二次封装SpringDataJPAMongoDB,打造更易用

  最近我在做一个新项目,由于我们项目组一直使用的是MongoDB数据库,所以新项目我就打算上SpringDataMongoDB尝试一下,虽然我早就用过了SpringDataJPA,对SpringData的相关CRUD和动态查询的封装也比较熟悉,但是自带的封装显然不能很好的满足我们的需求,本篇带大家讲述我所遇到的问题以及解决方案。
  注:MongoRepositoryJPARepository都继承自PagingAndSortingRepository,除了对应的数据库不同之外,功能都基本相同,所以本文的二次封装也可以用于JPARepository上。1。我遇到的问题
  问题一
  在SpringData中可以通过继承MongoRepositoryJPARepository接口的方式获得CRUD和分页的能力,但是这种能力也仅仅满足基础的CRUD操作和分页,对于极其常用的两个操作比如:针对数据库某个字段进行更新和多条件查询,这个接口并没有提供。
  准确的来说,多条件查询的能力是提供了,但是非常不宜用,它必须使用你的类做为查询条件,这个类的变量名还必须和数据库表中的字段名保持一致,这可以非常简单的让我们想到使用PO类当作这个查询条件。
  但是在有些规范中,PO类应该是一个拥有全参构造器的不可变类,这使得先创建这个类然后对应的查询字段进行赋值的操作变得不可行,这里我举一个简单的例子,我拥有一个数据表的映射对象:User,这就是俗称的PO。Document(user)classUser(Idvalid:String,valaccount:String,valpwd:String,valname:String,)复制代码
  然后我如果想要单独更新name这个字段时,我需要拥有整个User对象中的所有属性,因为Repository接口所提供的能力是把新增操作和更新操作放在一起的(save方法),每次更新都是所有字段的更新,这是我不愿意看到的,也是极其麻烦的。
  接着就是多条件查询的问题,我们先来看下如果我想要使用多条件查询,它的参数是什么:
  可以明显看到是一个叫Example的对象,如果我想使用,它应该是这样的:funtest(){valuserCssUser()user。name我要查询的参数具体值userRepository。findAll(Example。of(user))}复制代码
  这里我定义了一个CssUser去当它的查询条件的类,而且这个类和User类的内容几乎一样,因为我的User类是一个全参构造器没办法直接创建一个空对象进行赋值,所以我不得不创建一个CssUser去当查询条件的类,对于程序员来讲,这很烦。
  我想要的效果是什么样的呢?是这样的:funtest(){userRepository。listAll(Criteria。where(account)。is(admin)。and(name)。is(你的名字))}复制代码
  通过lambda的方式直接获取到某个属性的名字,然后作为查询变量,然后跟着链式调用可以随便在里面加上各样的查询条件,例子中的Criteria类是Spring已经为我们做好的,但是Repository接口并没有提供它,所以我们需要一层封装。
  问题二
  从上面的例子中我们可以看到在组装查询条件时,需要硬编码进去字段名,这对于程序员来说,是很烦的。
  所以我们应该使用lambda的特性,帮助我们去获取某一个类的字段名,通常是PO,因为它和数据库属性是一一对应的,整体要达到的有点像MybatisPLus的效果,大概是这样:funtest(){userRepository。listAll(Criteria。where(CssUser::account。mongoFiled())。is(admin)。and(CssUser::name。mongoFiled())。is(你的名字))}复制代码
  当然我的这个效果还没有MybatisPLus的效果好,它可以直接省略。mongoFiled()这个操作,这是因为我只加了三四行代码就能达到这个效果,对我而言够用了,而MybatisPLus则是有一套相关支持。
  虽然我这是Kotlin示例,但随后也会给出Java语法中的相关思路。2。Repository接口封装
  先来谈谈对CRUD的增强,正常情况下,我们只需要使用一个接口继承MongoRepository接口,然后SpringData就会帮我们生成一个动态代理类,并声明为Bean,直接注入就可以使用了,就像这样(代码中的:语法是继承的意思):interfaceUserMongoRepository:MongoRepositoryUser,String{}复制代码
  现在既然我们要对Repository进行增强,就需要再抽象出一个类,作为我们新的基类,之后的自己的业务类需要继承这个接口,而非原来的MongoRepository接口,当然,我们这个新的基类接口还会去继承MongoRepository接口,然后在接口中定义我们需要的新操作即可:NoRepositoryBeaninterfaceBaseMongoRepositoryT,ID:MongoRepositoryT,ID{funlistAll(condition:Criteria,pageable:Pageable):PageTfunupdateById(id:ID,update:Update):Long}复制代码
  我创建了一个新的接口:BaseMongoRepository,用它来继承MongoRepository,接着定义我们需要的扩展的一些方法,这里我扩展类了两个方法:新的多条件分页方法和新的更新接口。
  其中listAll方法的第一个参数Criteria是SpringData已经给我们提供好的类,它广泛运用于MongoTemplate里面,毕竟这层CRUD的封装底层其实还是MongoTemplate来操作。
  除了继承接口外,我们还需要对这两个方法进行实现,再创建一个BaseMongoRepository的实现类去继承MongoRepository的实现类SimpleMongoRepository:classBaseMongoRepositoryClassT,ID(privatevalmetadata:MongoEntityInformationT,ID,privatevalmongoOperations:MongoOperations):SimpleMongoRepositoryT,ID(metadata,mongoOperations),BaseMongoRepositoryT,ID{privatevalclazz:ClassTmetadata。javaTypeoverridefunlistAll(condition:Criteria,pageable:Pageable):PageT{vallistmongoOperations。find(Query(condition)。with(pageable),this。clazz,metadata。collectionName)returnPageableExecutionUtils。getPage(list,pageable){mongoOperations。count(Query(condition)。limit(1)。skip(1),clazz,metadata。collectionName)}}overridefunupdateById(id:ID,update:Update):Long{if(update。updateObject。isEmpty())return0returnmongoOperations。updateFirst(Query()。addCriteria(Criteria。where(id)。is(id)),update,metadata。collectionName)。modifiedCount}}复制代码
  其中BaseMongoRepositoryClass需要两个参数,这两个参数直接从SimpleMongoRepository里面拷贝过来然后通过构造再传递给SimpleMongoRepository即可,反正都是从自动注入里面来。
  两个变量简单讲解一下都是什么意思:MongoEntityInformation:这个是MongoEntity的元信息,就是最上面用Document注解标记的PO类的元信息,我们可以通过它拿到PO类的类型和数据表的名字。MongoOperations:MongoTemplate的实现类,这个我想不用多谈。
  接着就是方法实现,方法实现就是就是通过MongoTemplate操作了这个这个方法要做什么事,代码都比较简单因为不包含什么逻辑,熟悉MongoTemplate的一眼就可看懂。
  接下来就是最重要的一步,没有这一步一切都是白费,还会造成项目启动失败,那就是把这个新的基类告诉Spring,这是新的基类,你可以在项目的入口中加上这一句注解:EnableMongoRepositories(basePackages〔com。xxx。〕,repositoryBaseClassBaseMongoRepositoryClass::class)classAdminApplicationfunmain(args:ArrayString){runApplication(args)}复制代码
  指定一下repositoryBaseClass,这样生成动态代理的时候会以这个类为基类,我们动态代理类也就具有了我们定义的两个方法的能力了,使用中和原来的一样,只不过继承的接口不同罢了:interfaceUserRepository:BaseMongoRepositoryUser,String{}复制代码
  到这一步,我们可以完成这个效果:funtest(){userRepository。listAll(Criteria。where(account)。is(admin)。and(name)。is(你的名字))}复制代码3。实体类变量进行lambda封装
  接下来是对实体变量进行lambda封装,这个东西我觉得可以分为Kotlin和Java两个版本来说,两者各有千秋。
  先来说说Kotlin,因为Kotlin自身的语言特性的关系,实现起来比较简单,但也会拖一个尾巴,Kotlin具有一个扩展函数的能力,简单点说就是直接给某个类加上一些自定义方法,比如String我们可以在不继承的情况下直接给String类加上一个新的方法,然后它就会出现在String对象可调用的函数列表中。
  所以我们如果想要User::account。mongoFiled()这种效果,就得先知道User::account返回值是什么,在Kotlin中,它的返回值是一个KProperty类对象,那么我们直接给这个类加上扩展如下:funKProperty。mongoFiled():String{if(this。hasAnnotationId())returnidreturnthis。findAnnotationField()?。run{this。name。ifEmpty{thismongoFiled。name}}?:this。name}复制代码
  这样在lambda调用下就可以再调用这个方法了,接着来看看方法内容。首先判断了是否存在ID注解,这个ID注解是用来标识Mongo的主键属性的注解,这种注解标识的变量在数据库中统一叫做id,所以这里我也返回这个名字。接着判断是否存在Field注解,它是用来标识数据库字段和类变量不一样的情况,如果出现这种情况,我们使用注解所标识的字段名。最后,以上两种情况排除后,我们直接使用这个字段的名字。
  这样就可以达到如下效果了:funtest(){userRepository。listAll(Criteria。where(CssUser::account。mongoFiled())。is(admin)。and(CssUser::name。mongoFiled())。is(你的名字))}复制代码
  接着我们可以来说说Java的做法,首先也需要一个方法通过lambda拿到字段名,这个方法网上有很多我不再赘述,但是拿到之后该怎么办呢?
  你当然可以直接通过工具类的静态方法去拿,就像这样:funtest(){userRepository。listAll(Criteria。where(Util。getName(CssUser::account)。is(admin)。and(Util。getName(CssUser::name)。is(你的名字))}复制代码
  可能到这一步看起来还是略微不雅,追求极致的小伙伴这个时候就可以再度发挥封装的本色,将Criteria类封装出一个新的查询条件类,比如叫Condition,然后将Criteria装在里面再封装一下查询时的相关常用方法,就像这样(注意此处的Funtion入参只是一个例子,实际应该是泛型):publicclassCondition{privateCriteriacriterianewCriteria();publicConditionwhere(FunctionString,Stringfunction,Stringvalue){criteria。andOperator(Criteria。where(Util。getName(function))。is(value));returnthis;}}复制代码
  除了where方法你还可以继续封装gt、lt、or等常用方法,并且它们还能形成链式调用,最终的效果是这样的:publicstaticvoidmain(String〔〕args){CriteriacriterianewCondition()。where(CssUser::getName,你的名字)。where(CssUser::getAccount,admin);}复制代码
  是不是更优雅了呢?4。最后
  今天是满满的技术干货,希望Get到新技能的小伙伴可以积极的点赞,有什么问题都可以再评论区留言,我会积极对线的,下篇见。
  作者:和耳朵
  链接:https:juejin。cnpost7168133740093243423

诗歌原创你不解的风情,我写给你啊捣云立秋之前,我要生一场病缱绻的那种,介于既得与错失之间你要加紧替我治疗摘一些天边的云,昙花和马蹄草还要一串羞涩的野葡萄在黄昏的衣钵里,细细地捣碎月老屋东头,柚子树旁,有一湾鱼塘无人生,吃苦是磨炼,吃亏是福气人需要吃饭喝水,这是人的物质和生存需求人也需要吃苦吃亏,这是人的精神内在需求,也是人的本领素质需求,人是高级动物,人与动物的区别就在一个吃字上,人除了吃饭外还能够吃苦和吃亏,能够吃月色下的母亲河刘新华(北京)月色下的母亲河刘新华(北京)风起天际,犹如脱缰的野马呼啸掠过,打破沉寂的夜色沿月光映照的方向,开始搜寻搜寻那首黄土高坡,或青藏高原歌声唱响黄河长江浩荡的乡愁随蜿蜒的河水东流在祖国的小王子看东西只有用心才能看得清楚有些人有些书是你生命中注定要遇见的,躲不过绕不开,小王子就是这样的一个人一本书。再读小王子,深深地懂得生有涯而知无涯,未知永远多于已知,当我们自我感觉懂得越多时,越会发现自己的渺小放假通知!今早热搜霸屏,网友关注点都在它今早国庆放假安排冲上微博热搜第一阅读量超3亿据央视新闻,国务院办公厅发布2022年国庆节放假安排通知如下10月1日至7日放假调休,共7天10月8日(星期六)10月9日(星期日)上班珠江公园现鱼跃莲花引游客追拍鱼跃青池满,莺吟绿树低近日,唐代诗人李白作品晓晴中描写的情景出现在了广州珠江公园。莲花池边聚满众多摄影爱护者,争相抓拍鱼跃莲花的有趣现象。衔荷花花瓣最初可能只是鱼儿改善伙食的尝试,iPhone14将是印度越南出品?不再是国产的苹果,你还会喜欢吗?每年9月份,基本上都是各大手机厂商出新手机的时候,比如苹果,今年又是新的手机即将面世的时候,而对于很多果粉来说,那必须是1年1买的。但是,今年的情形又有些不一样了,到底哪里不一样呢低调的汕头富豪周奕丰22岁创业,44岁公司上市,如今身家130亿周奕丰01周奕丰,1969年5月出生在广东汕头,从澳门公开大学工商管理专业毕业后,来到广州市成禧经济发展有限公司工作。成禧公司的前身是成立于1979年的峡山塑料工艺厂,1987年工我喜欢你,一直都是好巧,我也是(已完结)想到了当年被我抛弃的学霸。我舔着脸上门了,那什么,辅导作业,被气得心梗的事儿考虑一下吗?呜呜,我被幼儿园卷麻了。专业的事儿得找孩子爹来办。1hr我是个一生要强的单亲妈妈,我努力赚钱500个品牌案例TheRow低调的极简轻奢品牌编辑安琪品牌名称TheRow成立时间2006年创始人Olsen姐妹总部地点美国纽约品牌理念量身定做的服装品牌故事TheRow背后的两位创始人MaryKateOlsen和Ashley酒馆战棋盘点那些酒馆消失的强力随从,有你喜欢的吗1漂浮观察者俗称小绿,和愤怒编织者小粉搭配养成很快,前期能拿到基本烂分就稳了。2族群领袖俗称狼爹,曾经传统野兽展开流的前中期核心,配上鼠群瞬间锁血。不过可惜本身不是野兽属性不能被B
多人运动速来!除了哈利波特,2月还有这些大作超期待好消息,再过一天,今年的第一个七天连班就结束了。另一个好消息,每年年初和年末都会迎来大作井喷,近几年的年度游戏也基本在二三月份发售。去年的2月可谓是百花齐放,老头环地平线2全战3等3。4下半角色池夜兰真简单分析,以及抽卡建议先祝夜兰的各位老公们在3。4下半,轻轻松松就把女神娶回家这3。4上半,我是一刻也待不下去了!ps以下纯属个人看法,有错误欢迎各位大佬指正,只求轻喷。全世界最飒的夜兰(姐就是女王,自MC!不倒!迷你没下架!原因是迷你总部炸了是一个MC玩家干的周围人努力的救火大家试图用干草汽油失败了消防车没赶到据说是司机开反了方向警官倒很快赶到并迅速封锁了现场没有放跑里面的一个人很好保护了知柏地黄丸丹栀逍遥丸,杞菊地黄丸,三个有啥区别?一文告诉你前几天,1位老家的朋友给我说自己睡不好觉,想用中药改善。但是,面对知柏地黄丸丹栀逍遥丸杞菊地黄丸,又觉得难以区分,所以想一起吃下去我觉得这不行。这三个的区别和辨证要点,还是要好好说俗语说一年之计在于春,养肝从立春开始,做好这4点大家好,我是中医高大夫。今天立春,俗语说一年之计在于春,对于人体来说,从立春开始,身体阳气复苏。自然界来讲,春是第一个开头,自然界的正常升发,带来了和谐的景象。从人体来讲,升发到位空腹不能喝牛奶吃香蕉?医生空腹真正不能吃的是这4类食物导语现如今绝大多数的人生活作息都是不太规律的,一天早中晚的三餐都不会按时去吃,在有的时候,很有可能一觉就睡到了大下午。肚子实在是饿的特别难受时,才会从床上慢慢悠悠的起来找一点东西去密闭的房间内把灯关闭马上漆黑一片,光都跑哪去了?光,只要没有被其他物体吸收,就会一直存在,不停地以光速飞行,永不停息。有光我们就能看到物体,理论上讲,如果有足够强大的天文望远镜,在2000多光年外的一颗星球上,就可能会看到秦始皇F1车手头盔加装摄像头,澳网推出元宇宙游戏iSports过去几年,奈飞出品的纪录片极速求生为F1的品牌带来了极大加成,根据F1官方给英国卫报的数据,纪录片在全球给F1带来了7300万新观众。而新赛季,国际汽联已经批准F1在车手头盔上安装今日元宵,喜乐团圆!今天灯火万家喜气盈庭又是一年元宵佳节明月皎皎千门秀华灯盏盏万户春自此良宵,大地春回癸卯春节画上了圆满的句号有人说,这个春节熟悉的年味回来了游子回到家乡卸下塞满思念的行囊阔别的亲人聚万向系民生人寿赚了5。8亿,1年关联交易累计金额超2亿作者韩紫竹编辑付影来源独角金融随着保险业2022年全年成绩单的揭晓,保费增长乏力投资收益滑落等问题困扰,让行业真切感受到去年逆境的阵痛与挣扎。与此同时,近期众多非上市险企们陆续披露大预言家李光耀?白手起家成新加坡国父,曾预言日本会走向平庸大预言家?白手起家的新加坡国父李光耀他早就预言了当今的国际局势,他断言日本会慢慢走向平庸。对于如今鹰酱和兔子的纷争,他也预见到了,并且认为兔子会在西太平洋的对抗中占据上风,当时有人
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网