一飞开源,介绍创意、新奇、有趣、实用的开源应用、系统、软件、硬件及技术,一个探索、发现、分享、使用与互动交流的开源技术社区平台。致力于打造活力开源社区,共建开源新生态!
一、开源项目简介
sqltoy-orm是比hibernate+myBatis(plus)更加贴合项目的orm框架(依赖spring),具有jpa式的对象CRUD的同时具有比myBatis(plus)更直观简洁性能强大的查询功能,越复杂优势越凸显。支持以下数据库:
使用Apache-2.0开源协议
技术架构
架构原则:sqltoy-orm是协助开发者的工具之一,目标是要解决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。
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究竟能给你带来什么?
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");
/** * @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));
//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>
<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>
select * from sqltoy_device_order_info t where #[t.ORGAN_ID in (:authedOrganIds)] #[and t.TRANS_DATE>=:beginDate] #[and t.TRANS_DATE<:endDate]
sqlToyLazyDao.findBySql(sql, MapKit.keys("authedOrganIds","beginDate", "endDate").values(authedOrganIdAry,beginDate,null), DeviceOrderInfoVO.class);
select * from sqltoy_device_order_info t where t.ORDER_ID=? and t.ORGAN_ID in (?,?,?) and t.TRANS_DATE>=?
<!-- 快速分页和分页优化演示 --> <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>
/** * 基于对象传参数模式 */ public void findPageByEntity() { StaffInfoVO staffVO = new StaffInfoVO(); // 作为查询条件传参数 staffVO.setStaffName("陈"); // 使用了分页优化器 // 第一次调用:执行count 和 取记录两次查询 // 第二次调用:在特定时效范围内count将从缓存获取,只会执行取单页记录查询 Page result = sqlToyLazyDao.findPageBySql(new Page(), "sqltoy_fastPage", staffVO); }
//支持对象属性注解模式进行缓存翻译 @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>
// 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);
<!-- 跨数据库函数自动替换(非必须项),适用于跨数据库软件产品,如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> -->
<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>
品类销售月份销售笔数销售数量(吨)销售金额(万元)苹果2019年5月1220002400苹果2019年4月1119002600苹果2019年3月1320002500香蕉2019年5月1020002000香蕉2019年4月1224002700香蕉2019年3月1323002700
<!-- 行转列 --> <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月笔数数量总金额笔数数量总金额笔数数量总金额香蕉132300270012240027001020002000苹果132000250011190026001220002400
<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>
品类销售月份销售笔数销售数量(吨)销售金额(万元)总计
711260014900小计
3659007500苹果2019年5月1220002400苹果2019年4月1119002600苹果2019年3月1320002500小计
3567007400香蕉2019年5月1020002000香蕉2019年4月1224002700香蕉2019年3月1323002700
<!-- 列与列环比演示 --> <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月笔数数量比上月总金额比上月笔数数量比上月总金额比上月笔数数量比上月总金额比上月香蕉132300
2700
1224004.30%27000.00%102000-16.70%2000-26.00%苹果132000
2500
111900-5.10%26004.00%1220005.20%2400-7.70%
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>
@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) @SqlToyEntity public class UserLogVO extends AbstractUserLogVO { private static final long serialVersionUID = 1296922598783858512L; /** default constructor */ public UserLogVO() { super(); } }
根据对象属性值,产生规则有序的ID,比如:订单类型为采购:P 销售:S,贸易类型:I内贸;O 外贸; 订单号生成规则为:1位订单类型+1位贸易类型+yyMMdd+3位流水(超过3位自动扩展) 最终会生成单号为:SI191120001
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" }) @EnableTransactionManagement public class SqlToyApplication { /** * @param args */ public static void main(String[] args) { SpringApplication.run(SqlToyApplication.class, args); } }
# sqltoy config spring.sqltoy.sqlResourcesDir=classpath:com/sqltoy/quickstart spring.sqltoy.translateConfig=classpath:sqltoy-translate.xml spring.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=200000
<?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>
@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); } }
<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>
评论