为什么说 sqltoy-orm 远比 mybatis 强大
sqltoy开源项目地址: https://github.com/chenrenfei/sagacity-sqltoy
sqltoy文档地址: https://chenrenfei.github.io/sqltoy/#/
先回答2个问题:
1、为什么对标mybatis?
我觉得作为开源orm项目如果不比mybatis强大的多(至少30%以上),就必须克制住那份show的冲动,不要给开发者添麻烦!996已经不易!
2、我个人觉得sqltoy跟mybatis比如何?
好太多了,单从sql写法上就无法对比。好吧,看过罗永浩跟王自如的pk视频吧,有一句说的好谁质疑谁举证,下面开始一一举证模式(如有质疑也请举证)
下面开始证明式:
- sqltoy是支持对象增删改和load的,类似于hibernate和jpa,有个别地方有些优势,如update操作规避了hibernate先load再update两次交互的缺陷。同时sqltoy也提供了quickvo工具,可以实现从数据库生产POJO。但这里我相信大家的差距不大,今天不作为重点讲解。其中有一个updateFetch方法是用于实现一次数据库交互完成锁记录、修改、并返回结果的操作,用于一些诸如库存台账、资金台账、订单台账、抢单等场景。
2.sqltoy的具有极其优雅的编写方式(说明一下sqltoy的sql是可以写在代码里面不一定只是写在xml中,简单的可以写代码中)
- mybatis的sql编写:这是一个工程化的写法,说的委婉点全是硬功夫!
select * from sqltoy_device_order_info t <where> <if test="orderId!=null"> and t.ORDER_ID=#{orderId} </if> <if test="authedOrganIds!=null"> and t.ORGAN_ID in <foreach collection="authedOrganIds" item="order_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>
- sqltoy的sql编写:简单直观、便于维护,开发和运维过程极为自然。
select * from sqltoy_device_order_info t where #[t.ORDER_ID=:orderId] #[and t.ORGAN_ID in (:authedOrganIds)] #[and t.STAFF_ID in (:staffIds)] #[and t.TRANS_DATE>=:beginDate] #[and t.TRANS_DATE<:endDate]
3、sqltoy拥有独特的缓存翻译功能,
感受sqltoy之美--缓存翻译、缓存条件检索
- 缓存翻译和缓存检索化繁为简---查询订单表(简化为单商品订单便于演示)
订单号 | 客户ID | 商品ID | 下单日期 | 商品数量 | 商品价格 | 订单金额 | 订单状态 | 业务员ID | 部门 |
---|---|---|---|---|---|---|---|---|---|
S0001 | C10001 | 101 | 2020-03-10 | 10 | 3000 | 30000 | 02 | 1001 | N002 |
-
要求查询日期在2020年1月10日至3月20日、客户名称中含<<星云科技>>字符的全部订单信息,要求显示商品名称、客户名称、业务员姓名、部门名称、订单状态中文
-
往常的做法:硬功夫硬碰硬系列
关联客户表做like
关联商品表查询品名
关联员工信息表显示员工名字
关联机构表显示机构名称
关联数据字典翻译状态
- 你是这么做的吗?看一下sqltoy怎么做吧!是不是变成了单表查询,效率毫无疑问秒杀多表关联无数倍!
<sql id="order_showcase"> <!-- 通过缓存对最终结果代码进行翻译,显示名称 --> <translate cache="organIdName" columns="ORGAN_NAME" /> <translate cache="staffIdName" columns="STAFF_NAME" /> <translate cache="goodsIdName" columns="GOODS_NAME" /> <translate cache="customIdName" columns="CUSTOM_NAME" /> <translate cache="dictKeyName" cache-type="ORDER_STATUS" columns="STATUS_NAME" /> <filters> <!-- 将查询参数customName通过缓存进行类似like检索获取匹配的customId数组作为查询条件 --> <cache-arg cache-name="customIdName" param="customName" alias-name="customIds" /> </filters> <value> <![CDATA[ select ORDER_ID , TOTAL_QUANTITY, TOTAL_AMT, ORGAN_ID , ORGAN_ID ORGAN_NAME,-- 缓存翻译 STAFF_ID , STAFF_ID STAFF_NAME, SIGN_TIME, CUSTOM_ID, CUSTOM_ID CUSTOM_NAME, GOODS_ID , GOODS_ID GOODS_NAME, STATUS, STATUS STATUS_NAME from od_order_info t1 where #[SIGN_TIME>=:beginTime] #[and SIGN_TIME <=:endTime] -- 这里就是缓存条件检索 #[and CUSTOM_ID in (:customIds)] ]]> </value> </sql>
4、sqltoy具有最高层级的分页优化(不提取count记录的另做讨论,绝大多数分页都是要提供用户符合条件记录总数预期的),sql分页的五个优化等级
- 第一级:将分页用不同数据库方言来封装。
- 第二级:考虑count查询的性能优化,如合理规避select 和from之间函数,order by 等跟count无关的sql执行,提升性能。
- 第三级:考虑join关联查询时,优先分页,将关联的数据范围缩小,从而提升关联查询的效率。
- 第四级:利用缓存机制避免每次count查询,从而提升分页的整体性能。
- 第五级:考虑极为特殊场景下分页的优化,第一:count语句可以自行定义;第二、考虑with table1 as (),table2 as() 等复杂场景下优化和适用。
<!-- 快速分页和分页优化演示 --> <sql id="sqltoy_fastPage"> <!-- 分页优化器,通过缓存实现查询条件一致的情况下在一定时间周期内缓存总记录数量,从而无需每次查询总记录数量 --> <!-- alive-max:最大存放多少个不同查询条件的总记录量; alive-seconds:查询条件记录量存活时长(比如120秒,超过阀值则重新查询) --> <page-optimize 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>
5、sqltoy是根本上避免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, new String[] { "authedOrganIds","beginDate", "endDate"}, new Object[] { 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) 设置条件值,不存在将条件直接作为字符串拼接为sql的一部分
6、sqltoy 提供了非常符合开发者的算法集成,如无限级的分组汇总求平均、行转列、列转行、同比环比计算,而这些往往都是普通开发者的弱点(靠sql写不出或者换了数据库又不通用)
-
感受sqltoy之美--数据旋转
- 水果销售记录表
品类 | 销售月份 | 销售笔数 | 销售数量(吨) | 销售金额(万元) |
---|---|---|---|---|
苹果 | 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 |
- sqltoy 实现行转列(category-columns 支持多列,即多维度分组如:
月份下面:5月(上旬、中旬、下旬)、4月(上旬、中旬、下旬)
电子产品例如: 手机(华为、三星、小米)、笔记本(苹果、联想、戴尔))
<!-- 列转行 --> <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>
- 效果:简单一个pivot代替了复杂的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 |
感受sqltoy之美--分组汇总
- 还是上面的苹果销售表格
<!-- 演示分组汇总 --> <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 |
感受sqltoy之美--环比计算
- 演示先行转列再环比计算
<!-- 列与列环比演示 --> <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% |
7、 sqltoy提供了常规的直接调用:如取随机记录、取top记录、唯一性校验(isUnique)、取总记录数等
/** * @todo 基于对象传参数模式(内部会根据sql中的参数提取对象对应属性的值) * @param <T> * @param sqlOrNamedSql * @param entity * @param topSize (大于1则取固定数量的记录,小于1,则表示按比例提取) * @return */ public <T extends Serializable> List<T> findTopBySql(final String sqlOrNamedSql, final T entity, final double topSize); public QueryResult getRandomResult(final QueryExecutor queryExecutor, final double randomCount); /** * @TODO 通过对象传参模式取随机记录 * @param <T> * @param sqlOrNamedSql * @param entity * @param randomCount * @return */ public <T extends Serializable> List<T> getRandomResult(final String sqlOrNamedSql, final T entity, final double randomCount); public <T> List<T> getRandomResult(final String sqlOrNamedSql, final String[] paramsNamed, final Object[] paramsValue, final Class<T> voClass, final double randomCount); /** * @todo 判断对象属性在数据库中是否唯一(已经考虑了是自身问题) * @param entity * @param paramsNamed * @return boolean true:唯一;false:不唯一 */ public boolean isUnique(Serializable entity, String[] paramsNamed);
8、 sqltoy提供了对树形结构表的跨数据库解决方案wrapTreeTableRoute,将表结构中统一构建节点路径、节点等级、是否叶子节点。
9、 sqltoy提供了分库分表功能,而且执行过程是并行计算的。
10、 sqltoy提供了统一字段赋值,解决创建时间、创建人、修改时间、修改人等信息的统一赋值。
11、 sqltoy提供了跨数据库函数自动替换功能,比如concat、ws_concat、decode、date_format、if、instr、nvl、sysdate 、length、to-char、to-number、trim 等函数的自动转换适配。有助于软件产品在一种数据库下开发,不同数据库下执行。
<bean id="sqlToyContext" class="org.sagacity.sqltoy.SqlToyContext" init-method="initialize" destroy-method="destroy"> <!-- 非必须属性:跨数据库函数自动替换(非必须项),适用于跨数据库软件产品,如mysql开发,oracle部署 --> <property name="functionConverts" value="default" /> <!-- 可以这样自行根据需要进行定义和扩展 <property name="functionConverts"> <list> <value>org.sagacity.sqltoy.plugins.function.impl.Nvl</value> <value>org.sagacity.sqltoy.plugins.function.impl.SubStr</value> <value>org.sagacity.sqltoy.plugins.function.impl.Now</value> <value>org.sagacity.sqltoy.plugins.function.impl.Length</value> </list> </property> --> </bean>
12、 sqltoy提供了sql变更检测自动加载功能,便于开发阶段减少重新加载;以及超时sql执行统计功能,便于找到慢sql进行优化。
13、提供了5种默认的非数据库相关的主键策略
-
shortNanoTime 22位有序安全ID,格式: 13位当前毫秒+6位纳秒+3位主机ID
-
nanoTimeId 26位有序安全ID,格式:15位:yyMMddHHmmssSSS+6位纳秒+2位(线程Id+随机数)+3位主机ID
-
uuid:32 位uuid
-
SnowflakeId 雪花算法ID
-
redisId 基于redis 来产生规则的ID主键
根据对象属性值,产生规则有序的ID,比如:订单类型为采购:P 销售:S,贸易类型:I内贸;O 外贸; 订单号生成规则为:1位订单类型+1位贸易类型+yyMMdd+3位流水(超过3位自动扩展) 最终会生成单号为:SI191120001
14、sqltoy是便于其他框架进行扩展式集成的
public class SqlToyContext implements ApplicationContextAware { /** * @todo 提供可以动态增加解析sql片段配置的接口,并返回具体id,用于第三方平台集成,如报表平台等 * @param sqlSegment * @return * @throws Exception */ public synchronized SqlToyConfig parseSqlSegment(Object sqlSegment) throws Exception { return scriptLoader.parseSqlSagment(sqlSegment); } /** * @todo 将构造好的SqlToyConfig放入缓存 * @param sqlToyConfig * @throws Exception */ public synchronized void putSqlToyConfig(SqlToyConfig sqlToyConfig) throws Exception { scriptLoader.putSqlToyConfig(sqlToyConfig); } }
sqltoy-orm已经在多家银行和企业应用,有兴趣我们可以一起来发展完善一个属于中国人的ORM框架!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
ImageMagick 7.0.10-5 发布,多功能图片处理软件
ImageMagick 7.0.10-5现已正式发布,该版本可以在Linux,Windows,Mac Os X,iOS,AndroidOS 等平台上运行。 ImageMagick 是一个用来创建、编辑、合成图片的软件。它可以读取、转换、写入超过 200 种格式的图片,包括 PNG、JPEG、GIF、HEIC、TIFF、DPX、EXR、WebP、Postscript、PDF 和 SVG 等等。 ImageMagick 可被用于图片切割、颜色替换、各种效果的应用,图片的旋转、组合,文本,直线, 多边形,椭圆,曲线,附加到图片伸展旋转等。支持Linux、Windows、Mac OS X、iOS、Android OS 平台。 7.0.10-5 版本的更新内容包括有: 默认 inkscape delegate to 版本 0.92(参考 https://github.com/ImageMagick/ImageMagick/discussions/1880)。 将单色图像深度设置为 1 以进行 Group4 压缩。 详细更新内容,以及了解以往版本更新可查阅更新记录。
- 下一篇
每日一博 | 干货|漫画算法:LRU 从实现到应用层层剖析
今天为大家分享很出名的LRU算法,第一讲共包括4节。 LRU概述 LRU使用 LRU实现 Redis近LRU概述 第一部分:LRU概述 LRU是Least Recently Used的缩写,译为最近最少使用。它的理论基础为“最近使用的数据会在未来一段时期内仍然被使用,已经很久没有使用的数据大概率在未来很长一段时间仍然不会被使用”由于该思想非常契合业务场景 ,并且可以解决很多实际开发中的问题,所以我们经常通过LRU的思想来作缓存,一般也将其称为LRU缓存机制。因为恰好leetcode上有这道题,所以我干脆把题目贴这里。但是对于LRU而言,希望大家不要局限于本题(大家不用担心学不会,我希望能做一个全网最简单的版本,希望可以坚持看下去!)下面,我们一起学习一下。 题目:运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作:获取数据 get 和 写入数据 put 。 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 写入数据 put(key, value) - 如果密钥不存在,则写入其数据...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker快速安装Oracle11G,搭建oracle11g学习环境