一飞开源,介绍创意、别致、有趣、适用的免费开源应用、系统、软件、硬件及手艺,一个摸索、发现、分享、利用与互动交换的开源手艺社区平台。努力于打造活力开源社区,共建开源重生态!

一、开源项目简介sqltoy-orm是什么

sqltoy-orm是比hibernate+myBatis(plus)愈加贴合项目标orm框架(依赖sPRing),具有jpa式的对象CRUD的同时具有比myBatis(plus)更曲不雅简洁性能强大的查询功用,越复杂优势越凸显。 撑持以下数据库:

oracle11g+、db2(9.5+)、sqlserver2012+、postgresql9.5+、mysql5.6+(mariadb/innosql)sqlite、H2DM达梦数据库、kingbaseelasticsearch 只撑持查询,版本撑持5.7+版本,建议利用7.3以上版本clickhouse、StarRocks、greenplum、impala(kudu)oceanBase、polardb、guassdb、tidbmongodb (只撑持查询)其他数据库撑持基于jdbc的sql施行(查询和自定义sql的施行)二、开源协议

利用Apache-2.0开源协议

三、界面展现

手艺架构

架构原则:sqltoy-orm是协助开发者的东西之一,目的是要处理99.5%的问题而不是笼盖100%的问题(目前从复杂的ERP项目、数据阐发报表项目标理论角度看根本100%笼盖)。

[开源]Java真正智慧的ORM框架,兼具Hibernate、MyBatis双重优点  第1张

四、功用概述

Java实正聪慧的ORM框架!撑持mysql、oracle、postgresql、sqlserver、db2、dm、mongodb、elasticsearch、tidb、kingbase、oceanbase、guassdb、greenplum、StarRocks、impala(kudu)、clickhouse、sqlite、h2、polardb。

sqltoy-orm是基于java语言开发的,兼有hibernate面向对象操做和myBatis灵敏查询的长处,同时更贴切项目、更贴切开发者的一个关系型数据库ORM框架,撑持oracle、mysql、postgresql、sqlserver、db2、sqlite、sybase iq、elasticsearch、mongodb等数据库。

与sqltoy-orm配套的有一个quickvo东西,协助通过数据库表产生POJO对象类。Quickvo东西摈斥了hibernate-tools东西的不敷(如模块化设置装备摆设、主键战略设置装备摆设、级联加载、修改、删除的逻辑),能够闪开发者通过设置装备摆设文件停止灵敏控造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即可完成全数操做 StaffInfoVO staffInfo = new StaffInfoVO(); //保留 sqlToyLazyDao.save(staffInfo); //删除 sqlToyLazyDao.delete(new StaffInfoVO("S2007")); //public Long update(Serializable entity, String... forceUpdateProps); // 那里对photo 属性停止强迫修改,其他为null主动会跳过 sqlToyLazyDao.update(staffInfo, "photo"); //深度修改,不论是否null全数字段修改 sqlToyLazyDao.updateDeeply(staffInfo); List<StaffInfoVO> staffList = new ArrayList<StaffInfoVO>(); StaffInfoVO staffInfo = new StaffInfoVO(); StaffInfoVO staffInfo1 = new StaffInfoVO(); 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文件中的sqlId/** * @todo 通过对象传参数,简化paramName[],paramValue[] 形式传参 * @param <T> * @param sqlOrNamedSql 能够是详细sql也能够是对应xml中的sqlId * @param entity 通过对象传参数,并按对象类型返回成果 */ public <T extends Serializable> List<T> findBySql(final String sqlOrNamedSql, final T entity);基于对象单表查询,并带缓存翻译public Page<StaffInfoVO> findStaff(Page<StaffInfoVO> pageModel, StaffInfoVO staffInfoVO) { // sql能够间接在代码中编写,复杂sql建议在xml中定义 // 单表entity查询场景下sql字段能够写成java类的属性名称 return findPageEntity(pageModel, StaffInfoVO.class, EntityQuery.create() .where("#[staffName like :staffName]#[and createTime>=:beginDate]#[and createTime<=:endDate]") .values(staffInfoVO));}对象式查询后修改或删除//演示代码中非间接sql形式设置前提形式停止记录修改public Long updateByQuery() { return sqlToyLazyDao.updateByQuery(StaffInfoVO.class, EntityUpdate.create().set("createBy", "S0001") .where("staffName like ?").values("张"));}//代码中非间接sql形式设置前提形式停止记录删除sqlToyLazyDao.deleteByQuery(StaffInfoVO.class, EntityQuery.create().where("status=?").values(0));2.3 极致朴实的sql编写体例sqltoy 的写法(一眼就看大白sql的本意,后面变动调整也十分便利,copy到数据库客户端里稍做出来即可施行)sqltoy前提组织原理很简单: 如 #[order_id=:orderId] 等于if(:orderId<>null) sql.apPEnd(order_id=:orderId);#[]内只要有一个参数为null即剔除撑持多层嵌套:如 #[and t.order_id=:orderId #[and t.order_type=:orderType]]前提判断保留#[@if(:param>=xx ||:param<=xx1) sql语句] 那种@if()高度灵敏形式,为特殊复杂场景下供给便当//1、 前提值处置跟详细sql别离//2、 将前提值前置通过filters 定义的通用办法加工规整(大大都是不需要额外处置的)<sql id="show_case"><filters> <!-- 参数statusAry只要包罗-1(代表全数)则将statusAry设置为null不参与前提检索 --> <eq params="statusAry" value="-1" /></filters><value><![CDATA[ select * from sqltoy_device_order_info t where #[t.status in (:statusAry)] #[and t.ORDER_ID=:orderId] #[and t.ORGAN_ID in (:authedOrganIds)] #[and t.STAFF_ID in (:staffIds)] #[and t.TRANS_DATE>=:beginAndEndDate[0]] #[and t.TRANS_DATE<:beginAndEndDate[1]] ]]></value></sql>同等功用mybatis写法<select id="show_case" resultMap="BaseResultMap"> select * from sqltoy_device_order_info t <where> <if test="statusAry!=null"> and t.status in <foreach collection="statusAry" item="status" separator="," open="(" close=")"> #{status} </foreach> </if> <if test="orderId!=null"> and t.ORDER_ID=#{orderId} </if> <if test="authedOrganIds!=null"> and t.ORGAN_ID in <foreach collection="authedOrganIds" item="organ_id" separator="," open="(" close=")"> #{order_id} </foreach> </if> <if test="staffIds!=null"> and t.STAFF_ID in <foreach collection="staffIds" item="staff_id" separator="," open="(" close=")"> #{staff_id} </foreach> </if> <if test="beginDate!=null"> and t.TRANS_DATE>=#{beginDate} </if> <if test="endDate!=null"> and t.TRANS_DATE<#{endDate} </if></where></select>2.4 天然避免sql注入,施行过程:假设sql语句如下select *from sqltoy_device_order_info t where #[t.ORGAN_ID in (:authedOrganIds)] #[and t.TRANS_DATE>=:beginDate] #[and t.TRANS_DATE<:endDate] java挪用过程sqlToyLazyDao.findBySql(sql, MapKit.keys("authedOrganIds","beginDate", "endDate").values(authedOrganIdAry,beginDate,null), DeviceOrderInfoVO.class);最末施行的sql是如许的:select *from sqltoy_device_order_info t where t.ORDER_ID=? and t.ORGAN_ID in (?,?,?) and t.TRANS_DATE>=? 然后通过: pst.set(index,value) 设置前提值2.5 最为极致的分页2.5.1 分页特点申明1、快速分页:@fast() 实现先取单页数据然后再联系关系查询,极大提拔速度。2、分页优化器:page-optimize 让分页查询由两次酿成1.3~1.5次(用缓存实现不异查询前提的总记录数量在必然周期内无需反复查询)3、sqltoy的分页取总记录的过程不是简单的select count(1) from (原始sql);而是智能判断能否酿成:select count(1) from 'from后语句', 并主动剔除最外层的order by4、sqltoy撑持并行查询:parallel="true",同时查询总记录数和单页数据,大幅提拔性能5、在极特殊情况下sqltoy分页考虑是更优化的,如:with t1 as (),t2 as @fast(select * from table1) select * from xxx 那种复杂查询的分页的处置,sqltoy的count查询会是:with t1 as () select count(1) from table1, 若是是:with t1 as @fast(select * from table1) select * from t1 ,count sql 就是:select count(1) from table12.5.2 分页sql示例<!-- 快速分页和分页优化演示 --><sql id="sqltoy_fastPage"> <!-- 分页优化器,通过缓存实现查询前提一致的情况下在必然时间周期内缓存总记录数量,从而无需每次查询总记录数量 --> <!-- parallel:能否并行查询总记录数和单页数据,当alive-max=1 时封闭缓存优化 --> <!-- alive-max:更大存放几个差别查询前提的总记录量; alive-seconds:查询前提记录量存活时长(好比120秒,超越阀值则从头查询) --> <page-optimize parallel="true" alive-max="100" alive-seconds="120" /> <value> <![CDATA[ select t1.*,t2.ORGAN_NAME -- @fast() 实现先分页取10条(详细数量由pageSize确定),然后再联系关系 from @fast(select t.* from sqltoy_staff_info t where t.STATUS=1 #[and t.STAFF_NAME like :staffName] order by t.ENTRY_DATE desc ) t1 left join sqltoy_organ_info t2 on t1.organ_id=t2.ORGAN_ID ]]> </value> <!-- 那里为极特殊情况下供给了自定义count-sql来实现极致性能优化 --> <!-- <count-sql></count-sql> --></sql>2.5.3 分页java代码挪用/** * 基于对象传参数形式 */public void findPageByEntity() { StaffInfoVO staffVO = new StaffInfoVO(); // 做为查询前提传参数 staffVO.setStaffName("陈"); // 利用了分页优化器 // 第一次挪用:施行count 和 取记录两次查询 // 第二次挪用:在特按时效范畴内count将从缓存获取,只会施行取单页记录查询 Page result = sqlToyLazyDao.findPageBySql(new Page(), "sqltoy_fastPage", staffVO);}2.6 极为巧妙的缓存翻译,将多表联系关系查询尽量酿成单表1、 通过缓存翻译: 将代码转化为名称,制止联系关系查询,极大简化sql并提拔查询效率2、 通过缓存名称模糊婚配: 获取精准的编码做为前提,制止联系关系like 模糊查询//撑持对象属性注解形式停止缓存翻译@Translate(cacheName = "dictKeyName", cacheType = "DEVICE_TYPE", keyField = "deviceType")private String deviceTypeName;@Translate(cacheName = "staffIdName", keyField = "staffId")private String staffName;<sql id="sqltoy_order_search"> <!-- 缓存翻译设备类型 cache:详细的缓存定义的名称, cache-type:一般针对数据字典,供给一个分类前提过滤 columns:sql中的查询字段名称,能够逗号分隔对多个字段停止翻译 cache-indexs:缓存数据名称对应的列,不填则默认为第二列(从0起头,1则暗示第二列), 例如缓存的数据构造是:key、name、fullName,则第三列暗示全称 --> <translate cache="dictKeyName" cache-type="DEVICE_TYPE" columns="deviceTypeName" cache-indexs="1"/> <!-- 员工名称翻译,若是统一个缓存则能够同时对几个字段停止翻译 --> <translate cache="staffIdName" columns="staffName,createName" /> <filters> <!-- 反向操纵缓存通过名称婚配出id用于切确查询 --> <cache-arg cache-name="staffIdNameCache" param="staffName" alias-name="staffIds"/> </filters> <value> <![CDATA[ select ORDER_ID, DEVICE_TYPE, DEVICE_TYPE deviceTypeName,-- 设备分类名称 STAFF_ID, STAFF_ID staffName, -- 员工姓名 ORGAN_ID, CREATE_BY, CREATE_BY createName -- 创建人名称 from sqltoy_device_order_info t where #[t.ORDER_ID=:orderId] #[and t.STAFF_ID in (:staffIds)] ]]> </value></sql>2.7 并行查询接口标准// parallQuery 面向查询(不要用于事务操做过程中),sqltoy供给强大的办法,但能否得当利用需要利用者做合理的判断/** * @TODO 并行查询并返回一维List,有几个查询List中就包罗几个成果对象,paramNames和paramValues是全数sql的前提参数的合集 * @param parallQueryList * @param paramNames * @param paramValues */public <T> List<QueryResult<T>> parallQuery(List<ParallQuery> parallQueryList, String[] paramNames, Object[] paramValues);利用规范//定义参数String[] paramNames = new String[] { "userId", "defaultRoles", "deployId", "authObjType" };Object[] paramValues = new Object[] { userId, defaultRoles, GlobalConstants.DEPLOY_ID, SagacityConstants.TempAuthObjType.GROUP };// 利用并行查询同时施行2个sql,前提参数是2个查询的合集List<QueryResult<TreeModel>> list = super.parallQuery( Arrays.asList( ParallQuery.create().sql("webframe_searchAllModuleMenus").resultType(TreeModel.class), ParallQuery.create().sql("webframe_searchAllUserReports").resultType(TreeModel.class)), paramNames, paramValues); 2.8 跨数据库撑持1、供给类似hibernate性量的对象操做,主动生成响应数据库的方言。2、供给了常用的:分页、取top、取随机记录等查询,制止了各自差别数据库差别的写法。3、供给了树形构造表的尺度钻取查询体例,取代以往的递归查询,一种体例适配所有数据库。4、sqltoy供给了大量基于算法的辅助实现,较大水平上用算法取代了以往的sql,实现了跨数据库5、sqltoy供给了函数替代功用,好比能够让oracle的语句在mysql或sqlserver上施行(sql加载时将函数替代成了mysql的函数),较大水平上实现了代码的产物化。 default:SubStr\Trim\Instr\Concat\Nvl 函数;能够拜见org.sagacity.sqltoy.plugins.function.Nvl 代码实现 <!-- 跨数据库函数主动替代(非必需项),适用于跨数据库软件产物,如mysql开发,oracle摆设 --> <property name="functionConverts" value="default"> <!-- 也能够如许自行按照需要停止定义和扩展 <property name="functionConverts"> <list> <value>org.sagacity.sqltoy.plugins.function.Nvl</value> <value>org.sagacity.sqltoy.plugins.function.SubStr</value> <value>org.sagacity.sqltoy.plugins.function.Now</value> <value>org.sagacity.sqltoy.plugins.function.Length</value> </list> </property> -->6、通过sqlId+dialect形式,可针对特定命据库写sql,sqltoy按照数据库类型获取现实施行sql,挨次为: dialect_sqlId->sqlId_dialect->sqlId, 如数据库为mysql,挪用sqlId:sqltoy_showcase,则现实施行:sqltoy_showcase_mysql <sql id="sqltoy_showcase"> <value> <![CDATA[ select * from sqltoy_user_log t where t.user_id=:userId ]]> </value> </sql> <!-- sqlId_数据库方言(小写) --> <sql id="sqltoy_showcase_mysql"> <value> <![CDATA[ select * from sqltoy_user_log t where t.user_id=:userId ]]> </value> </sql>2.9 供给行列转换、分组汇总、同比环比等生果销售记录表

品类

销售月份

销售笔数

销售数量(吨)

销售金额(万元)

苹果

2019年5月

12

2000

2400

苹果

2019年4月

11

1900

2600

苹果

2019年3月

13

2000

2500

香蕉

2019年5月

10

2000

2000

香蕉

2019年4月

12

2400

2700

香蕉

2019年3月

13

2300

2700

2.9.1 行转列(列转行也撑持)<!-- 行转列 --><sql id="pivot_case"> <value> <![CDATA[ select t.fruit_name,t.order_month,t.sale_count,t.sale_quantity,t.total_amt from sqltoy_fruit_order t order by t.fruit_name ,t.order_month ]]> </value> <!-- 行转列,将order_month做为分类横向题目,从sale_count列到total_amt 三个目标扭转成行 --> <pivot start-column="sale_count" end-column="total_amt" group-columns="fruit_name" category-columns="order_month" /></sql>效果

品类

2019年3月

2019年4月

2019年5月

笔数

数量

总金额

笔数

数量

总金额

笔数

数量

总金额

香蕉

13

2300

2700

12

2400

2700

10

2000

2000

苹果

13

2000

2500

11

1900

2600

12

2000

2400

2.9.2 分组汇总、求均匀(可肆意层级)<sql id="group_summary_case"> <value> <![CDATA[ select t.fruit_name,t.order_month,t.sale_count,t.sale_quantity,t.total_amt from sqltoy_fruit_order t order by t.fruit_name ,t.order_month ]]> </value> <!-- reverse 能否反向 --> <summary columns="sale_count,sale_quantity,total_amt" reverse="true"> <!-- 层级挨次连结从高到低 --> <global sum-label="总计" label-column="fruit_name" /> <group group-column="fruit_name" sum-label="小计" label-column="fruit_name" /> </summary></sql>效果

品类

销售月份

销售笔数

销售数量(吨)

销售金额(万元)

总计

71

12600

14900

小计

36

5900

7500

苹果

2019年5月

12

2000

2400

苹果

2019年4月

11

1900

2600

苹果

2019年3月

13

2000

2500

小计

35

6700

7400

香蕉

2019年5月

10

2000

2000

香蕉

2019年4月

12

2400

2700

香蕉

2019年3月

13

2300

2700

2.9.3 先行转列再环比计算<!-- 列与列环比演示 --><sql id="cols_relative_case"> <value> <![CDATA[ select t.fruit_name,t.order_month,t.sale_count,t.sale_amt,t.total_amt from sqltoy_fruit_order t order by t.fruit_name ,t.order_month ]]> </value> <!-- 数据扭转,行转列,将order_month 按列显示,每个月份下面有三个目标 --> <pivot start-column="sale_count" end-column="total_amt" group-columns="fruit_name" category-columns="order_month" /> <!-- 列与列之间停止环比计算 --> <cols-chain-relative group-size="3" relative-indexs="1,2" start-column="1" format="#.00%" /></sql>效果

品类

2019年3月

2019年4月

2019年5月

笔数

数量

比上月

总金额

比上月

笔数

数量

比上月

总金额

比上月

笔数

数量

比上月

总金额

比上月

香蕉

13

2300

2700

12

2400

4.30%

2700

0.00%

10

2000

-16.70%

2000

-26.00%

苹果

13

2000

2500

11

1900

-5.10%

2600

4.00%

12

2000

5.20%

2400

-7.70%

2.10 分库分表2.10.1 查询分库分表(分库和分表战略能够同时利用) sql拜见quickstart项目:com/sqltoy/quickstart/sqltoy-quickstart.sql.xml 文件 <!-- 演示分库 --> <sql id="qstart_db_sharding_case"> <sharding-datasource strategy="hashDataSource" params="userId" /> <value> <![CDATA[ select * from sqltoy_user_log t -- userId 做为分库关键字段属于必备前提 where t.user_id=:userId #[and t.log_date>=:beginDate] #[and t.log_date<=:endDate] ]]> </value> </sql> <!-- 演示分表 --> <sql id="qstart_sharding_table_case"> <sharding-table tables="sqltoy_trans_info_15d" strategy="realHisTable" params="beginDate" /> <value> <![CDATA[ select * from sqltoy_trans_info_15d t where t.trans_date>=:beginDate #[and t.trans_date<=:endDate] ]]> </value> </sql> 2.10.2 操做分库分表(vo对象由quickvo东西主动按照数据库生成,且自定义的注解不会被笼盖)

@Sharding 在对象上通过注解来实现分库分表的战略设置装备摆设

拜见:com.sqltoy.quickstart.ShardingSearchTest 停止演示

package com.sqltoy.showcase.vo;import java.time.LocalDate;import java.time.LocalDateTime;import org.sagacity.sqltoy.config.annotation.Sharding;import org.sagacity.sqltoy.config.annotation.SqlToyEntity;import org.sagacity.sqltoy.config.annotation.Strategy;import com.sagframe.sqltoy.showcase.vo.base.AbstractUserLogVO;/* * db则是分库战略设置装备摆设,table 则是分表战略设置装备摆设,能够同时设置装备摆设也能够独立设置装备摆设 * 战略name要跟spring中的bean定义name一致,fields暗示要以对象的哪几个字段值做为判断根据,能够一个或多个字段 * maxConcurrents:可选设置装备摆设,暗示更大并行数 maxWaitSeconds:可选设置装备摆设,暗示更大期待秒数 */@Sharding(db = @Strategy(name = "hashBalanceDBSharding", fields = { "userId" }), // table = @Strategy(name = "hashBalanceSharding", fields = {"userId" }), maxConcurrents = 10, maxWaitSeconds = 1800)@SqlToyEntitypublic class UserLogVO extends AbstractUserLogVO { private static final long serialVersionUID = 1296922598783858512L; /** default constructor */ public UserLogVO() { super(); }}2.11 五种非数据库相关主键生成战略(可自扩展)主键战略除了数据库自带的 sequence\identity 外包罗以下数据库无关的主键战略。通过quickvo设置装备摆设,主动生成在VO对象中。2.11.1 shortNanoTime 22位有序平安ID,格局: 13位当前毫秒+6位纳秒+3位主机ID2.11.2 nanoTimeId 26位有序平安ID,格局:15位:yyMMddHHmmssSSS+6位纳秒+2位(线程Id+随机数)+3位主机ID2.11.3 uuid:32 位uuid2.11.4 SnowflakeId 雪花算法ID2.11.5 redisId 基于redis 来产生规则的ID主键

按照对象属性值,产生规则有序的ID,好比:订单类型为采购:P 销售:S,商业类型:I内贸;O 外贸; 订单号生陈规则为:1位订单类型+1位商业类型+yyMMdd+3位流水(超越3位主动扩展) 最末会生成单号为:SI191120001

2.12 elastic原生查询撑持2.13 elasticsearch-sql 插件形式sql形式撑持2.14 sql文件变动主动重载,便利开发和调试2.15 公共字段同一赋值,针对创建人、创建时间、修改人、修改时间等2.16 供给了查询成果日期、数字格局化、平安脱敏处置,让复杂的工作变得简单3.集成申明3.1 拜见trunk 下面的quickstart,并阅读readme.md停止上手package com.sqltoy.quickstart;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.ComponentScan;import org.springframework.transaction.annotation.EnableTransactionManagement;/** * * @project sqltoy-quickstart * @description quickstart 主法式入口 * @author zhongxuchen * @version v1.0, Date:2020年7月17日 * @modify 2020年7月17日,修改申明 */@SpringBootApplication@ComponentScan(basePackages = { "com.sqltoy.config", "com.sqltoy.quickstart" })@EnableTransactionManagementpublic class SqlToyApplication { /** * @param args */ public static void main(String[] args) { SpringApplication.run(SqlToyApplication.class, args); }}3.2 application.properties sqltoy部门设置装备摆设# sqltoy configspring.sqltoy.sqlResourcesDir=classpath:com/sqltoy/quickstartspring.sqltoy.translateConfig=classpath:sqltoy-translate.xmlspring.sqltoy.debug=true#spring.sqltoy.reservedWords=status,sex_type#dataSourceSelector: org.sagacity.sqltoy.plugins.datasource.impl.DefaultDataSourceSelector#spring.sqltoy.defaultDataSource=dataSource# 供给同一公共字段赋值(源码拜见quickstart)spring.sqltoy.unifyFieldsHandler=com.sqltoy.plugins.SqlToyUnifyFieldsHandler#spring.sqltoy.printSqlTimeoutMillis=2000003.3 缓存翻译的设置装备摆设文件sqltoy-translate.xml<?xml version="1.0" encoding="UTF-8"?><sagacity xmlns="http://www.sagframe.com/schema/sqltoy-translate" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sagframe.com/schema/sqltoy-translate http://www.sagframe.com/schema/sqltoy/sqltoy-translate.xsd"> <!-- 缓存有默认失效时间,默认为1小时,因而只要较为频繁的缓存才需要及时检测 --> <cache-translates> <!-- 基于sql间接查询的体例获取缓存 --> <sql-translate cache="dictKeyName" datasource="dataSource"> <sql> <![CDATA[ select t.DICT_KEY,t.DICT_NAME,t.STATUS from SQLTOY_DICT_DETAIL t where t.DICT_TYPE=:dictType order by t.SHOW_INDEX ]]> </sql> </sql-translate> <!-- 员工ID和姓名的缓存 --> <sql-translate cache="staffIdName" datasource="dataSource"> <sql> <![CDATA[ select STAFF_ID,STAFF_NAME,STATUS from SQLTOY_STAFF_INFO ]]> </sql> </sql-translate> <!-- 机构号和机构名称的缓存 --> <sql-translate cache="organIdName" datasource="dataSource"> <sql> <![CDATA[ select ORGAN_ID,ORGAN_NAME from SQLTOY_ORGAN_INFO order by SHOW_INDEX ]]> </sql> </sql-translate> </cache-translates> <!-- 缓存刷新检测,能够供给多个基于sql、service、rest办事检测 --> <cache-update-checkers> <!-- 基于sql的缓存更新检测 --> <sql-increment-checker cache="organIdName" check-frequency="60" datasource="dataSource"> <sql><![CDATA[ --#not_debug#-- select ORGAN_ID,ORGAN_NAME from SQLTOY_ORGAN_INFO where UPDATE_TIME >=:lastUpdateTime ]]></sql> </sql-increment-checker> <!-- 增量更新,检测到变革间接更新缓存 --> <sql-increment-checker cache="staffIdName" check-frequency="30" datasource="dataSource"> <sql><![CDATA[ --#not_debug#-- select STAFF_ID,STAFF_NAME,STATUS from SQLTOY_STAFF_INFO where UPDATE_TIME >=:lastUpdateTime ]]></sql> </sql-increment-checker> <!-- 增量更新,带有内部门类的查询成果第一列是分类 --> <sql-increment-checker cache="dictKeyName" check-frequency="15" has-inside-group="true" datasource="dataSource"> <sql><![CDATA[ --#not_debug#-- select t.DICT_TYPE,t.DICT_KEY,t.DICT_NAME,t.STATUS from SQLTOY_DICT_DETAIL t where t.UPDATE_TIME >=:lastUpdateTime ]]></sql> </sql-increment-checker> </cache-update-checkers></sagacity>现实营业开发利用,间接操纵SqlToyCRUDService 就能够停止常规的操做,制止简单的对象操做本身写service, 别的针对复杂逻辑则本身写service间接通过挪用sqltoy供给的:SqlToyLazyDao 完成数据库交互操做!@RunWith(SpringRunner.class)@SpringBootTest(classes = SqlToyApplication.class)public class CruDCaseServiceTest { @Autowired private SqlToyCRUDService sqlToyCRUDService; /** * 创建一条员工记录 */ @Test public void saveStaffInfo() { StaffInfoVO staffInfo = new StaffInfoVO(); staffInfo.setStaffId("S190715005"); staffInfo.setStaffCode("S190715005"); staffInfo.setStaffName("测试员工4"); staffInfo.setSexType("M"); staffInfo.setEmail("test3@aliyun.com"); staffInfo.setEntryDate(LocalDate.now()); staffInfo.setStatus(1); staffInfo.setOrganId("C0001"); staffInfo.setPhoto(FileUtil.readAsBytes("classpath:/mock/staff_photo.jpg")); staffInfo.setCountry("86"); sqlToyCRUDService.save(staffInfo); } }4. sqltoy sql关键申明4.1 sqltoy sql最简单规则#[] 对称符号#[] 等于if(中间语句参数能否有null)? true: 剔除#[] 整块代码,false:拿掉#[ 和 ] ,将中间的sql做为施行的一部门。#[] 撑持嵌套,如#[t.status=:status #[and t.createDate>=:createDate]] 会先从内而外施行if(null)逻辑操纵filters前提值预处置实现判断null的同一,下面是sqltoy完好供给的前提过滤器和其他函数 不要被大段的申明吓一跳,99%都用不上,一般filters里面只会用到eq 和 to-date<sql id="show_case"> <!-- 通过filters里面的逻辑将查询前提转为null,部门逻辑则对参数停止二次转换 默认前提参数为空白、空集合、空数组都转为null parmas 暗示能够用逗号写多个参数,param 暗示只撑持单个参数 --> <filters> <!-- 等于,如机构类别前端传负一就转为null不参与前提过滤 --> <eq params="organType" value="-1" /> <!-- 前提值在某个区间则转为null --> <between params="" start-value="0" end-value="9999" /> <!-- 将参数前提值转换为日期格局,format能够是yyyy-MM-dd那种自定义格局也能够是: first_of_day:月的第一天;last_of_day:月的最初一天,first_of_year:年的第一天,last_of_year年的最初一天,increment-unit默认为days --> <to-date params="" format="yyyyMMdd" increment-time="1" increment-unit="days"/> <!-- 将参数转为数字 --> <to-number params="" data-type="decimal" /> <!-- 将前端传过来的字符串切割成数组 --> <split data-type="string" params="staffAuthOrgs" split-sign=","/> <!-- 小于等于 --> <lte params="" value="" /> <!-- 小于 --> <lt params="" value="" /> <!-- 大于等于 --> <gte params="" value="" /> <!-- 大于 --> <gt params="" value="" /> <!-- 字符替代,默认按照正则表达停止全数替代,is-first为true时只替代首个 --> <replace params="" regex="" value="" is-first="false" /> <!-- 首要参数,即当某个参数不为null时,excludes是指被排除之外的参数全数为null --> <primary param="orderId" excludes="organIds" /> <!-- 排他性参数,当某个参数是xxx值时,将其他参数设置为特定值 --> <exclusive param="" compare-type="eq" compare-values="" set-params="" set-value="" /> <!-- 通过缓存停止文字模糊婚配获取切确的代码值参与切确查询 --> <cache-arg cache-name="" cache-type="" param="" cache-mapping-indexes="" alias-name=""/> <!-- 将数组转化成in 的参数前提并增加单引号 --> <to-in-arg params=""/> </filters> <!-- 缓存翻译,能够多个,uncached-template 是针对未能婚配时显示的弥补,${value} 暗示显示key值,能够key=[${value}不决义 那种写法 --> <translate cache="dictKeyName" cache-type="POST_TYPE" columns="POST_TYPE" cache-indexs="1" uncached-template=""/> <!-- 平安掩码:tel\姓名\地址\卡号 --> <!--最简单用法: <secure-mask columns="" type="tel"/> --> <secure-mask columns="" type="name" head-size="3" tail-size="4" mask-code="*****" mask-rate="50" /> <!-- 分库战略 --> <sharding-datasource strategy="" /> <!-- 分表战略 --> <sharding-table tables="" strategy="" params="" /> <!-- 分页优化,缓存不异查询前提的分页总记录数量, alive-max:暗示不异的一个sql保留100个差别前提查询 alive-seconds:不异的查询前提分页总记录数保留时长(单元秒) --> <page-optimize alive-max="100" alive-seconds="600" /> <!-- 日期格局化 --> <date-format columns="" format="yyyy-MM-dd HH:mm:ss"/> <!-- 数字格局 --> <number-format columns="" format=""/> <value> <![CDATA[ select t1.*,t2.ORGAN_NAME from @fast(select * from sys_staff_info t where #[t.sexType=:sexType] #[and t.JOIN_DATE>:beginDate] #[and t.STAFF_NAME like :staffName] -- 能否虚拟员工@if()做逻辑判断 #[@if(:isVirtual==true||:isVirtual==0) and t.IS_VIRTUAL=1] ) t1,sys_organ_info t2 where t1.ORGAN_ID=t2.ORGAN_ID ]]> </value> <!-- 为极致分页供给自定义写sql --> <count-sql><![CDATA[]]></count-sql> <!-- 汇总和求均匀,通过算法实现复杂的sql,同时能够酿成数据库无关 --> <summary columns="" radix-size="2" reverse="false" sum-site="left"> <global sum-label="" label-column="" /> <group sum-label="" label-column="" group-column="" /> </summary> <!-- 拼接某列,mysql中等同于group_concat\oracle 中的WMSYS.WM_CONCAT功用 --> <link sign="," column="" /> <!-- 行转列 (跟unpivot互斥),算法实现数据库无关 --> <pivot category-columns="" group-columns="" start-column="" end-column="" default-value="0" /> <!-- 列转行 --> <unpivot columns="" values-as-column="" /></sql>5. sqltoy关键代码申明5.1 sqltoy-orm 次要分以下几个部门: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/