开源Java真正智慧的ORM框架,兼具HibernateMy
一飞开源,介绍创意、新奇、有趣、实用的免费开源应用、系统、软件、硬件及技术,一个探索、发现、分享、使用与互动交流的开源技术社区平台。致力于打造活力开源社区,共建开源新生态!一、开源项目简介sqltoyorm是什么
sqltoyorm是比hibernatemyBatis(plus)更加贴合项目的orm框架(依赖spring),具有jpa式的对象CRUD的同时具有比myBatis(plus)更直观简洁性能强大的查询功能,越复杂优势越凸显。支持以下数据库:oracle11g、db2(9。5)、sqlserver2012、postgresql9。5、mysql5。6(mariadbinnosql)sqlite、H2DM达梦数据库、kingbaseelasticsearch只支持查询,版本支持5。7版本,建议使用7。3以上版本clickhouse、StarRocks、greenplum、impala(kudu)oceanBase、polardb、guassdb、tidbmongodb(只支持查询)其他数据库支持基于jdbc的sql执行(查询和自定义sql的执行)二、开源协议
使用Apache2。0开源协议三、界面展示
技术架构
架构原则:sqltoyorm是协助开发者的工具之一,目标是要解决99。5的问题而不是覆盖100的问题(目前从复杂的ERP项目、数据分析报表项目的实践角度看基本100覆盖)。
四、功能概述
Java真正智慧的ORM框架!支持mysql、oracle、postgresql、sqlserver、db2、dm、mongodb、elasticsearch、tidb、kingbase、oceanbase、guassdb、greenplum、StarRocks、impala(kudu)、clickhouse、sqlite、h2、polardb。
sqltoyorm是基于java语言开发的,兼有hibernate面向对象操作和myBatis灵活查询的优点,同时更贴切项目、更贴切开发者的一个关系型数据库ORM框架,支持oracle、mysql、postgresql、sqlserver、db2、sqlite、sybaseiq、elasticsearch、mongodb等数据库。
与sqltoyorm配套的有一个quickvo工具,协助通过数据库表产生POJO对象类。Quickvo工具摈弃了hibernatetools工具的不足(如模块化配置、主键策略配置、级联加载、修改、删除的逻辑),可以让开发者通过配置文件进行灵活控制POJO的生成,而不用担心自己对POJO类的修改被覆盖。
sqltoy最大的特点在于贴切项目、了解开发者,试图实实在在的帮助开发者简化数据库交互过程中的大量看似很重要其实是机械的重复工作,同时sqltoy将大量第一手项目最佳实践模式抽象成工具带给开发者。
sqltoy究竟能给你带来什么?有别于hibernate的增删改(含批量和级联)内部实现,hibernate修改对象个别属性是不是要先load后修改防止其他字段被置为null?高并发大集群下面就会冲掉别人修改的数据。堪称最为优雅的动态sql查询写法,一眼就可以看明白sql的业务含义,后期维护更容易,更容易进行sql优化和调整。提供快速分页查询可能实现先分页后关联,减少关联数据规模。让你极大减少表关联、让sql更简洁清晰并提升性能的缓存翻译功能。让你不需要太牛的sql能力实现行转列、列转行。提供多重分组汇总求平均的功能(算法和sql的结合,以强补弱,简单而优雅)让分页查询可以只需1。45次,你的分页是不是2次查询(一次查总记录数、一次查实际记录)?支持并行查询特性(4。17。13版本支持并行分页查询)提供分库分表sharding功能,为高性能、分布式场景奠定基础。最大程度实现跨数据库能力,提供不同数据库函数动态替换,尤其针对软件产品一个sql就可以适应不同数据库。Sql语句支持注释,即客户端调试好即可copy进来。机理是加载sql时剔除注释,但会保留hint形式的数据库自身优化性注释。五、技术选型1、基于Java语言开发2。快速特点说明2。1对象操作跟jpa类似并有针对性加强(包括级联)通过quickvo工具从数据库生成对应的POJO,引入sqlltoy自带的SqlToyLazyDao即可完成全部操作StaffInfoVOstaffInfonewStaffInfoVO();保存sqlToyLazyDao。save(staffInfo);删除sqlToyLazyDao。delete(newStaffInfoVO(S2007));publicLongupdate(Serializableentity,String。。。forceUpdateProps);这里对photo属性进行强制修改,其他为null自动会跳过sqlToyLazyDao。update(staffInfo,photo);深度修改,不管是否null全部字段修改sqlToyLazyDao。updateDeeply(staffInfo);ListStaffInfoVOstaffListnewArrayListStaffInfoVO();StaffInfoVOstaffInfonewStaffInfoVO();StaffInfoVOstaffInfo1newStaffInfoVO();staffList。add(staffInfo);staffList。add(staffInfo1);批量保存或修改sqlToyLazyDao。saveOrUpdateAll(staffList);批量保存sqlToyLazyDao。saveAll(staffList);。。。。。。。。。。。。。。。sqlToyLazyDao。loadByIds(StaffInfoVO。class,S2007)唯一性验证sqlToyLazyDao。isUnique(staffInfo,staffCode);2。2支持代码中对象查询sqltoy中统一的规则是代码中可以直接传sql也可以是对应xml文件中的sqlIdtodo通过对象传参数,简化paramName〔〕,paramValue〔〕模式传参paramTparamsqlOrNamedSql可以是具体sql也可以是对应xml中的sqlIdparamentity通过对象传参数,并按对象类型返回结果publicTextendsSerializableListTfindBySql(finalStringsqlOrNamedSql,finalTentity);基于对象单表查询,并带缓存翻译publicPageStaffInfoVOfindStaff(PageStaffInfoVOpageModel,StaffInfoVOstaffInfoVO){sql可以直接在代码中编写,复杂sql建议在xml中定义单表entity查询场景下sql字段可以写成java类的属性名称returnfindPageEntity(pageModel,StaffInfoVO。class,EntityQuery。create()。where(〔staffNamelike:staffName〕〔andcreateTime:beginDate〕〔andcreateTime:endDate〕)。values(staffInfoVO));}对象式查询后修改或删除演示代码中非直接sql模式设置条件模式进行记录修改publicLongupdateByQuery(){returnsqlToyLazyDao。updateByQuery(StaffInfoVO。class,EntityUpdate。create()。set(createBy,S0001)。where(staffNamelike?)。values(张));}代码中非直接sql模式设置条件模式进行记录删除sqlToyLazyDao。deleteByQuery(StaffInfoVO。class,EntityQuery。create()。where(status?)。values(0));2。3极致朴素的sql编写方式sqltoy的写法(一眼就看明白sql的本意,后面变更调整也非常便捷,copy到数据库客户端里稍做出来即可执行)sqltoy条件组织原理很简单:如〔orderid:orderId〕等于if(:orderIdnull)sql。append(orderid:orderId);〔〕内只要有一个参数为null即剔除支持多层嵌套:如〔andt。orderid:orderId〔andt。ordertype:orderType〕〕条件判断保留〔if(:paramxx:paramxx1)sql语句〕这种if()高度灵活模式,为特殊复杂场景下提供便利1、条件值处理跟具体sql分离2、将条件值前置通过filters定义的通用方法加工规整(大多数是不需要额外处理的)sqlidshowcasefilters!参数statusAry只要包含1(代表全部)则将statusAry设置为null不参与条件检索eqparamsstatusAryvalue1filtersvalue!〔CDATA〔selectfromsqltoydeviceorderinfotwhere〔t。statusin(:statusAry)〕〔andt。ORDERID:orderId〕〔andt。ORGANIDin(:authedOrganIds)〕〔andt。STAFFIDin(:staffIds)〕〔andt。TRANSDATE:beginAndEndDate〔0〕〕〔andt。TRANSDATE:beginAndEndDate〔1〕〕〕〕valuesql同等功能mybatis写法selectidshowcaseresultMapBaseResultMapselectfromsqltoydeviceorderinfotwhereifteststatusAry!nullandt。statusinforeachcollectionstatusAryitemstatusseparator,open(close){status}foreachififtestorderId!nullandt。ORDERID{orderId}ififtestauthedOrganIds!nullandt。ORGANIDinforeachcollectionauthedOrganIdsitemorganidseparator,open(close){orderid}foreachififteststaffIds!nullandt。STAFFIDinforeachcollectionstaffIdsitemstaffidseparator,open(close){staffid}foreachififtestbeginDate!nullandt。TRANSDATE{beginDate}ififtestendDate!nullandt。TRANSDATE{endDate}ifwhereselect2。4天然防止sql注入,执行过程:假设sql语句如下selectfromsqltoydeviceorderinfotwhere〔t。ORGANIDin(:authedOrganIds)〕〔andt。TRANSDATE:beginDate〕〔andt。TRANSDATE:endDate〕java调用过程sqlToyLazyDao。findBySql(sql,MapKit。keys(authedOrganIds,beginDate,endDate)。values(authedOrganIdAry,beginDate,null),DeviceOrderInfoVO。class);最终执行的sql是这样的:selectfromsqltoydeviceorderinfotwheret。ORDERID?andt。ORGANIDin(?,?,?)andt。TRANSDATE?然后通过:pst。set(index,value)设置条件值2。5最为极致的分页2。5。1分页特点说明1、快速分页:fast()实现先取单页数据然后再关联查询,极大提升速度。2、分页优化器:pageoptimize让分页查询由两次变成1。31。5次(用缓存实现相同查询条件的总记录数量在一定周期内无需重复查询)3、sqltoy的分页取总记录的过程不是简单的selectcount(1)from(原始sql);而是智能判断是否变成:selectcount(1)fromfrom后语句,并自动剔除最外层的orderby4、sqltoy支持并行查询:paralleltrue,同时查询总记录数和单页数据,大幅提升性能5、在极特殊情况下sqltoy分页考虑是最优化的,如:witht1as(),t2asfast(selectfromtable1)selectfromxxx这种复杂查询的分页的处理,sqltoy的count查询会是:witht1as()selectcount(1)fromtable1,如果是:witht1asfast(selectfromtable1)selectfromt1,countsql就是:selectcount(1)fromtable12。5。2分页sql示例!快速分页和分页优化演示sqlidsqltoyfastPage!分页优化器,通过缓存实现查询条件一致的情况下在一定时间周期内缓存总记录数量,从而无需每次查询总记录数量!parallel:是否并行查询总记录数和单页数据,当alivemax1时关闭缓存优化!alivemax:最大存放多少个不同查询条件的总记录量;aliveseconds:查询条件记录量存活时长(比如120秒,超过阀值则重新查询)pageoptimizeparalleltruealivemax100aliveseconds120value!〔CDATA〔selectt1。,t2。ORGANNAMEfast()实现先分页取10条(具体数量由pageSize确定),然后再关联fromfast(selectt。fromsqltoystaffinfotwheret。STATUS1〔andt。STAFFNAMElike:staffName〕orderbyt。ENTRYDATEdesc)t1leftjoinsqltoyorganinfot2ont1。organidt2。ORGANID〕〕value!这里为极特殊情况下提供了自定义countsql来实现极致性能优化!countsqlcountsqlsql2。5。3分页java代码调用基于对象传参数模式publicvoidfindPageByEntity(){StaffInfoVOstaffVOnewStaffInfoVO();作为查询条件传参数staffVO。setStaffName(陈);使用了分页优化器第一次调用:执行count和取记录两次查询第二次调用:在特定时效范围内count将从缓存获取,只会执行取单页记录查询PageresultsqlToyLazyDao。findPageBySql(newPage(),sqltoyfastPage,staffVO);}2。6极为巧妙的缓存翻译,将多表关联查询尽量变成单表1、通过缓存翻译:将代码转化为名称,避免关联查询,极大简化sql并提升查询效率2、通过缓存名称模糊匹配:获取精准的编码作为条件,避免关联like模糊查询支持对象属性注解模式进行缓存翻译Translate(cacheNamedictKeyName,cacheTypeDEVICETYPE,keyFielddeviceType)privateStringdeviceTypeName;Translate(cacheNamestaffIdName,keyFieldstaffId)privateStringstaffName;sqlidsqltoyordersearch!缓存翻译设备类型cache:具体的缓存定义的名称,cachetype:一般针对数据字典,提供一个分类条件过滤columns:sql中的查询字段名称,可以逗号分隔对多个字段进行翻译cacheindexs:缓存数据名称对应的列,不填则默认为第二列(从0开始,1则表示第二列),例如缓存的数据结构是:key、name、fullName,则第三列表示全称translatecachedictKeyNamecachetypeDEVICETYPEcolumnsdeviceTypeNamecacheindexs1!员工名称翻译,如果同一个缓存则可以同时对几个字段进行翻译translatecachestaffIdNamecolumnsstaffName,createNamefilters!反向利用缓存通过名称匹配出id用于精确查询cacheargcachenamestaffIdNameCacheparamstaffNamealiasnamestaffIdsfiltersvalue!〔CDATA〔selectORDERID,DEVICETYPE,DEVICETYPEdeviceTypeName,设备分类名称STAFFID,STAFFIDstaffName,员工姓名ORGANID,CREATEBY,CREATEBYcreateName创建人名称fromsqltoydeviceorderinfotwhere〔t。ORDERID:orderId〕〔andt。STAFFIDin(:staffIds)〕〕〕valuesql2。7并行查询接口规范parallQuery面向查询(不要用于事务操作过程中),sqltoy提供强大的方法,但是否恰当使用需要使用者做合理的判断TODO并行查询并返回一维List,有几个查询List中就包含几个结果对象,paramNames和paramValues是全部sql的条件参数的合集paramparallQueryListparamparamNamesparamparamValuespublicTListQueryResultTparallQuery(ListParallQueryparallQueryList,String〔〕paramNames,Object〔〕paramValues);使用范例定义参数String〔〕paramNamesnewString〔〕{userId,defaultRoles,deployId,authObjType};Object〔〕paramValuesnewObject〔〕{userId,defaultRoles,GlobalConstants。DEPLOYID,SagacityConstants。TempAuthObjType。GROUP};使用并行查询同时执行2个sql,条件参数是2个查询的合集ListQueryResultTreeModellistsuper。parallQuery(Arrays。asList(ParallQuery。create()。sql(webframesearchAllModuleMenus)。resultType(TreeModel。class),ParallQuery。create()。sql(webframesearchAllUserReports)。resultType(TreeModel。class)),paramNames,paramValues);2。8跨数据库支持1、提供类似hibernate性质的对象操作,自动生成相应数据库的方言。2、提供了常用的:分页、取top、取随机记录等查询,避免了各自不同数据库不同的写法。3、提供了树形结构表的标准钻取查询方式,代替以往的递归查询,一种方式适配所有数据库。4、sqltoy提供了大量基于算法的辅助实现,较大程度上用算法代替了以往的sql,实现了跨数据库5、sqltoy提供了函数替换功能,比如可以让oracle的语句在mysql或sqlserver上执行(sql加载时将函数替换成了mysql的函数),较大程度上实现了代码的产品化。default:SubStrTrimInstrConcatNvl函数;可以参见org。sagacity。sqltoy。plugins。function。Nvl代码实现!跨数据库函数自动替换(非必须项),适用于跨数据库软件产品,如mysql开发,oracle部署propertynamefunctionConvertsvaluedefault!也可以这样自行根据需要进行定义和扩展propertynamefunctionConvertslistvalueorg。sagacity。sqltoy。plugins。function。Nvlvaluevalueorg。sagacity。sqltoy。plugins。function。SubStrvaluevalueorg。sagacity。sqltoy。plugins。function。Nowvaluevalueorg。sagacity。sqltoy。plugins。function。Lengthvaluelistproperty6、通过sqlIddialect模式,可针对特定数据库写sql,sqltoy根据数据库类型获取实际执行sql,顺序为:dialectsqlIdsqlIddialectsqlId,如数据库为mysql,调用sqlId:sqltoyshowcase,则实际执行:sqltoyshowcasemysqlsqlidsqltoyshowcasevalue!〔CDATA〔selectfromsqltoyuserlogtwheret。userid:userId〕〕valuesql!sqlId数据库方言(小写)sqlidsqltoyshowcasemysqlvalue!〔CDATA〔selectfromsqltoyuserlogtwheret。userid:userId〕〕valuesql2。9提供行列转换、分组汇总、同比环比等水果销售记录表
品类
销售月份
销售笔数
销售数量(吨)
销售金额(万元)
苹果
2019年5月
12hr2000hr2400hr苹果
2019年4月
11hr1900hr2600hr苹果
2019年3月
13hr2000hr2500hr香蕉
2019年5月
10hr2000hr2000hr香蕉
2019年4月
12hr2400hr2700hr香蕉
2019年3月
13hr2300hr27002。9。1行转列(列转行也支持)!行转列sqlidpivotcasevalue!〔CDATA〔selectt。fruitname,t。ordermonth,t。salecount,t。salequantity,t。totalamtfromsqltoyfruitordertorderbyt。fruitname,t。ordermonth〕〕value!行转列,将ordermonth作为分类横向标题,从salecount列到totalamt三个指标旋转成行pivotstartcolumnsalecountendcolumntotalamtgroupcolumnsfruitnamecategorycolumnsordermonthsql效果
品类
2019年3月
2019年4月
2019年5月
笔数
数量
总金额
笔数
数量
总金额
笔数
数量
总金额
香蕉
13hr2300hr2700hr12hr2400hr2700hr10hr2000hr2000hr苹果
13hr2000hr2500hr11hr1900hr2600hr12hr2000hr24002。9。2分组汇总、求平均(可任意层级)sqlidgroupsummarycasevalue!〔CDATA〔selectt。fruitname,t。ordermonth,t。salecount,t。salequantity,t。totalamtfromsqltoyfruitordertorderbyt。fruitname,t。ordermonth〕〕value!reverse是否反向summarycolumnssalecount,salequantity,totalamtreversetrue!层级顺序保持从高到低globalsumlabel总计labelcolumnfruitnamegroupgroupcolumnfruitnamesumlabel小计labelcolumnfruitnamesummarysql效果
品类
销售月份
销售笔数
销售数量(吨)
销售金额(万元)
总计
71hr12600hr14900hr小计
36hr5900hr7500hr苹果
2019年5月
12hr2000hr2400hr苹果
2019年4月
11hr1900hr2600hr苹果
2019年3月
13hr2000hr2500hr小计
35hr6700hr7400hr香蕉
2019年5月
10hr2000hr2000hr香蕉
2019年4月
12hr2400hr2700hr香蕉
2019年3月
13hr2300hr27002。9。3先行转列再环比计算!列与列环比演示sqlidcolsrelativecasevalue!〔CDATA〔selectt。fruitname,t。ordermonth,t。salecount,t。saleamt,t。totalamtfromsqltoyfruitordertorderbyt。fruitname,t。ordermonth〕〕value!数据旋转,行转列,将ordermonth按列显示,每个月份下面有三个指标pivotstartcolumnsalecountendcolumntotalamtgroupcolumnsfruitnamecategorycolumnsordermonth!列与列之间进行环比计算colschainrelativegroupsize3relativeindexs1,2startcolumn1format。00sql效果
品类
2019年3月
2019年4月
2019年5月
笔数
数量
比上月
总金额
比上月
笔数
数量
比上月
总金额
比上月
笔数
数量
比上月
总金额
比上月
香蕉
13hr2300hr2700hr12hr2400hr4。30
2700hr0。00
10hr2000hr16。70
2000hr26。00
苹果
13hr2000hr2500hr11hr1900hr5。10
2600hr4。00
12hr2000hr5。20
2400hr7。702。10分库分表2。10。1查询分库分表(分库和分表策略可以同时使用)sql参见quickstart项目:comsqltoyquickstartsqltoyquickstart。sql。xml文件!演示分库sqlidqstartdbshardingcaseshardingdatasourcestrategyhashDataSourceparamsuserIdvalue!〔CDATA〔selectfromsqltoyuserlogtuserId作为分库关键字段属于必备条件wheret。userid:userId〔andt。logdate:beginDate〕〔andt。logdate:endDate〕〕〕valuesql!演示分表sqlidqstartshardingtablecaseshardingtabletablessqltoytransinfo15dstrategyrealHisTableparamsbeginDatevalue!〔CDATA〔selectfromsqltoytransinfo15dtwheret。transdate:beginDate〔andt。transdate:endDate〕〕〕valuesql2。10。2操作分库分表(vo对象由quickvo工具自动根据数据库生成,且自定义的注解不会被覆盖)
Sharding在对象上通过注解来实现分库分表的策略配置
参见:com。sqltoy。quickstart。ShardingSearchTest进行演示packagecom。sqltoy。showcase。vo;importjava。time。LocalDate;importjava。time。LocalDateTime;importorg。sagacity。sqltoy。config。annotation。Sharding;importorg。sagacity。sqltoy。config。annotation。SqlToyEntity;importorg。sagacity。sqltoy。config。annotation。Strategy;importcom。sagframe。sqltoy。showcase。vo。base。AbstractUserLogVO;db则是分库策略配置,table则是分表策略配置,可以同时配置也可以独立配置策略name要跟spring中的bean定义name一致,fields表示要以对象的哪几个字段值作为判断依据,可以一个或多个字段maxConcurrents:可选配置,表示最大并行数maxWaitSeconds:可选配置,表示最大等待秒数Sharding(dbStrategy(namehashBalanceDBSharding,fields{userId}),tableStrategy(namehashBalanceSharding,fields{userId}),maxConcurrents10,maxWaitSeconds1800)SqlToyEntitypublicclassUserLogVOextendsAbstractUserLogVO{privatestaticfinallongserialVersionUID1296922598783858512L;defaultconstructorpublicUserLogVO(){super();}}2。11五种非数据库相关主键生成策略(可自扩展)主键策略除了数据库自带的sequenceidentity外包含以下数据库无关的主键策略。通过quickvo配置,自动生成在VO对象中。2。11。1shortNanoTime22位有序安全ID,格式:13位当前毫秒6位纳秒3位主机ID2。11。2nanoTimeId26位有序安全ID,格式:15位:yyMMddHHmmssSSS6位纳秒2位(线程Id随机数)3位主机ID2。11。3uuid:32位uuid2。11。4SnowflakeId雪花算法ID2。11。5redisId基于redis来产生规则的ID主键
根据对象属性值,产生规则有序的ID,比如:订单类型为采购:P销售:S,贸易类型:I内贸;O外贸;订单号生成规则为:1位订单类型1位贸易类型yyMMdd3位流水(超过3位自动扩展)最终会生成单号为:SI1911200012。12elastic原生查询支持2。13elasticsearchsql插件模式sql模式支持2。14sql文件变更自动重载,方便开发和调试2。15公共字段统一赋值,针对创建人、创建时间、修改人、修改时间等2。16提供了查询结果日期、数字格式化、安全脱敏处理,让复杂的事情变得简单3。集成说明3。1参见trunk下面的quickstart,并阅读readme。md进行上手packagecom。sqltoy。quickstart;importorg。springframework。boot。SpringApplication;importorg。springframework。boot。autoconfigure。SpringBootApplication;importorg。springframework。context。annotation。ComponentScan;importorg。springframework。transaction。annotation。EnableTransactionManagement;projectsqltoyquickstartdescriptionquickstart主程序入口authorzhongxuchenversionv1。0,Date:2020年7月17日modify2020年7月17日,修改说明SpringBootApplicationComponentScan(basePackages{com。sqltoy。config,com。sqltoy。quickstart})EnableTransactionManagementpublicclassSqlToyApplication{paramargspublicstaticvoidmain(String〔〕args){SpringApplication。run(SqlToyApplication。class,args);}}3。2application。propertiessqltoy部分配置sqltoyconfigspring。sqltoy。sqlResourcesDirclasspath:comsqltoyquickstartspring。sqltoy。translateConfigclasspath:sqltoytranslate。xmlspring。sqltoy。debugtruespring。sqltoy。reservedWordsstatus,sextypedataSourceSelector:org。sagacity。sqltoy。plugins。datasource。impl。DefaultDataSourceSelectorspring。sqltoy。defaultDataSourcedataSource提供统一公共字段赋值(源码参见quickstart)spring。sqltoy。unifyFieldsHandlercom。sqltoy。plugins。SqlToyUnifyFieldsHandlerspring。sqltoy。printSqlTimeoutMillis2000003。3缓存翻译的配置文件sqltoytranslate。xmllt;?xmlversion1。0encodingUTF8?sagacityxmlnshttp:www。sagframe。comschemasqltoytranslatexmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:www。sagframe。comschemasqltoytranslatehttp:www。sagframe。comschemasqltoysqltoytranslate。xsd!缓存有默认失效时间,默认为1小时,因此只有较为频繁的缓存才需要及时检测cachetranslates!基于sql直接查询的方式获取缓存sqltranslatecachedictKeyNamedatasourcedataSourcesql!〔CDATA〔selectt。DICTKEY,t。DICTNAME,t。STATUSfromSQLTOYDICTDETAILtwheret。DICTTYPE:dictTypeorderbyt。SHOWINDEX〕〕sqlsqltranslate!员工ID和姓名的缓存sqltranslatecachestaffIdNamedatasourcedataSourcesql!〔CDATA〔selectSTAFFID,STAFFNAME,STATUSfromSQLTOYSTAFFINFO〕〕sqlsqltranslate!机构号和机构名称的缓存sqltranslatecacheorganIdNamedatasourcedataSourcesql!〔CDATA〔selectORGANID,ORGANNAMEfromSQLTOYORGANINFOorderbySHOWINDEX〕〕sqlsqltranslatecachetranslates!缓存刷新检测,可以提供多个基于sql、service、rest服务检测cacheupdatecheckers!基于sql的缓存更新检测sqlincrementcheckercacheorganIdNamecheckfrequency60datasourcedataSourcesql!〔CDATA〔notdebugselectORGANID,ORGANNAMEfromSQLTOYORGANINFOwhereUPDATETIME:lastUpdateTime〕〕sqlsqlincrementchecker!增量更新,检测到变化直接更新缓存sqlincrementcheckercachestaffIdNamecheckfrequency30datasourcedataSourcesql!〔CDATA〔notdebugselectSTAFFID,STAFFNAME,STATUSfromSQLTOYSTAFFINFOwhereUPDATETIME:lastUpdateTime〕〕sqlsqlincrementchecker!增量更新,带有内部分类的查询结果第一列是分类sqlincrementcheckercachedictKeyNamecheckfrequency15hasinsidegrouptruedatasourcedataSourcesql!〔CDATA〔notdebugselectt。DICTTYPE,t。DICTKEY,t。DICTNAME,t。STATUSfromSQLTOYDICTDETAILtwheret。UPDATETIME:lastUpdateTime〕〕sqlsqlincrementcheckercacheupdatecheckerssagacity实际业务开发使用,直接利用SqlToyCRUDService就可以进行常规的操作,避免简单的对象操作自己写service,另外针对复杂逻辑则自己写service直接通过调用sqltoy提供的:SqlToyLazyDao完成数据库交互操作!RunWith(SpringRunner。class)SpringBootTest(classesSqlToyApplication。class)publicclassCrudCaseServiceTest{AutowiredprivateSqlToyCRUDServicesqlToyCRUDService;创建一条员工记录TestpublicvoidsaveStaffInfo(){StaffInfoVOstaffInfonewStaffInfoVO();staffInfo。setStaffId(S190715005);staffInfo。setStaffCode(S190715005);staffInfo。setStaffName(测试员工4);staffInfo。setSexType(M);staffInfo。setEmail(test3aliyun。com);staffInfo。setEntryDate(LocalDate。now());staffInfo。setStatus(1);staffInfo。setOrganId(C0001);staffInfo。setPhoto(FileUtil。readAsBytes(classpath:mockstaffphoto。jpg));staffInfo。setCountry(86);sqlToyCRUDService。save(staffInfo);}}4。sqltoysql关键说明4。1sqltoysql最简单规则〔〕对称符号〔〕等于if(中间语句参数是否有null)?true:剔除〔〕整块代码,false:拿掉〔和〕,将中间的sql作为执行的一部分。〔〕支持嵌套,如〔t。status:status〔andt。createDate:createDate〕〕会先从内而外执行if(null)逻辑利用filters条件值预处理实现判断null的统一,下面是sqltoy完整提供的条件过滤器和其他函数不要被大段的说明吓一跳,99都用不上,正常filters里面只会用到eq和todatesqlidshowcase!通过filters里面的逻辑将查询条件转为null,部分逻辑则对参数进行二次转换默认条件参数为空白、空集合、空数组都转为nullparmas表示可以用逗号写多个参数,param表示只支持单个参数filters!等于,如机构类别前端传负一就转为null不参与条件过滤eqparamsorganTypevalue1!条件值在某个区间则转为nullbetweenparamsstartvalue0endvalue9999!将参数条件值转换为日期格式,format可以是yyyyMMdd这种自定义格式也可以是:firstofday:月的第一天;lastofday:月的最后一天,firstofyear:年的第一天,lastofyear年的最后一天,incrementunit默认为daystodateparamsformatyyyyMMddincrementtime1incrementunitdays!将参数转为数字tonumberparamsdatatypedecimal!将前端传过来的字符串切割成数组splitdatatypestringparamsstaffAuthOrgssplitsign,!小于等于lteparamsvalue!小于ltparamsvalue!大于等于gteparamsvalue!大于gtparamsvalue!字符替换,默认根据正则表达进行全部替换,isfirst为true时只替换首个replaceparamsregexvalueisfirstfalse!首要参数,即当某个参数不为null时,excludes是指被排除之外的参数全部为nullprimaryparamorderIdexcludesorganIds!排他性参数,当某个参数是xxx值时,将其他参数设置为特定值exclusiveparamcomparetypeeqcomparevaluessetparamssetvalue!通过缓存进行文字模糊匹配获取精确的代码值参与精确查询cacheargcachenamecachetypeparamcachemappingindexesaliasname!将数组转化成in的参数条件并增加单引号toinargparamsfilters!缓存翻译,可以多个,uncachedtemplate是针对未能匹配时显示的补充,{value}表示显示key值,可以key〔{value}未定义这种写法translatecachedictKeyNamecachetypePOSTTYPEcolumnsPOSTTYPEcacheindexs1uncachedtemplate!安全掩码:tel姓名地址卡号!最简单用法:securemaskcolumnstypetelsecuremaskcolumnstypenameheadsize3tailsize4maskcodemaskrate50!分库策略shardingdatasourcestrategy!分表策略shardingtabletablesstrategyparams!分页优化,缓存相同查询条件的分页总记录数量,alivemax:表示相同的一个sql保留100个不同条件查询aliveseconds:相同的查询条件分页总记录数保留时长(单位秒)pageoptimizealivemax100aliveseconds600!日期格式化dateformatcolumnsformatyyyyMMddHH:mm:ss!数字格式numberformatcolumnsformatvalue!〔CDATA〔selectt1。,t2。ORGANNAMEfromfast(selectfromsysstaffinfotwhere〔t。sexType:sexType〕〔andt。JOINDATE:beginDate〕〔andt。STAFFNAMElike:staffName〕是否虚拟员工if()做逻辑判断〔if(:isVirtualtrue:isVirtual0)andt。ISVIRTUAL1〕)t1,sysorganinfot2wheret1。ORGANIDt2。ORGANID〕〕value!为极致分页提供自定义写sqlcountsql!〔CDATA〔〕〕countsql!汇总和求平均,通过算法实现复杂的sql,同时可以变成数据库无关summarycolumnsradixsize2reversefalsesumsiteleftglobalsumlabellabelcolumngroupsumlabellabelcolumngroupcolumnsummary!拼接某列,mysql中等同于groupconcatoracle中的WMSYS。WMCONCAT功能linksign,column!行转列(跟unpivot互斥),算法实现数据库无关pivotcategorycolumnsgroupcolumnsstartcolumnendcolumndefaultvalue0!列转行unpivotcolumnsvaluesascolumnsql5。sqltoy关键代码说明5。1sqltoyorm主要分以下几个部分:SqlToyDaoSupport:提供给开发者Dao继承的基本Dao,集成了所有对数据库操作的方法。SqlToyLazyDao:提供给开发者快捷使用的Dao,让开发者只关注写Service业务逻辑代码,在service中直接调用lazyDaoSqltoyCRUDService:简单Service的封装,面向controller层提供基于对象的快捷service调用,比如save(pojo)这种极为简单的就无需再写service代码DialectFactory:数据库方言工厂类,sqltoy根据当前连接的方言调用不同数据库的实现封装。SqlToyContext:sqltoy上下文配置,是整个框架的核心配置和交换区,spring配置主要是配置sqltoyContext。EntityManager:封装于SqlToyContext,用于托管POJO对象,建立对象跟数据库表的关系。sqltoy通过SqlToyEntity注解扫描加载对象。ScriptLoader:sql配置文件加载解析器,封装于SqlToyContext中。sql文件严格按照。sql。xml规则命名。TranslateManager:缓存翻译管理器,用于加载缓存翻译的xml配置文件和缓存实现类,sqltoy提供了接口并提供了默认基于ehcache的本地缓存实现,这样效率是最高的,而redis这种分布式缓存IO开销太大,缓存翻译是一个高频度的调用,一般会缓存注入员工、机构、数据字典、产品品类、地区等相对变化不频繁的稳定数据。ShardingStragety:分库分表策略管理器,4。x版本之后策略管理器并不需要显式定义,只有通过spring定义,sqltoy会在使用时动态管理。5。2快速阅读理解sqltoy:从SqlToyLazyDao作为入口,了解sqltoy提供的所有功能SqlToyDaoSupport是SqlToyLazyDao具体功能实现。从DialectFactory会进入不同数据库方言的实现入口。可以跟踪看到具体数据库的实现逻辑。你会看到oracle、mysql等分页、取随机记录、快速分页的封装等。EntityManager:你会找到如何扫描POJO并构造成模型,知道通过POJO操作数据库实质会变成相应的sql进行交互。ParallelUtils:对象分库分表并行执行器,通过这个类你会看到分库分表批量操作时如何将集合分组到不同的库不同的表并进行并行调度的。SqlToyContext:sqltoy配置的上下文,通过这个类可以看到sqltoy全貌。PageOptimizeUtils:可以看到分页优化默认实现原理。六、源码地址
访问一飞开源:https:code。exmay。com