The changelog is maintained in Chinese. An English translation may be added in the future; version numbers, class names and code identifiers remain readable as-is.
Release Notes
This document records user-visible changes in each jkit release. Each version number appears once, listed from newest to oldest.
Unreleased changes are appended to the current version section (currently 2.0.1); once released, that section is frozen and a new version section is added above it. Do not rewrite frozen historical versions, and do not open a new unreleased heading for the same version number.
2.0.1 - 2026-09-13
The repository has been split into multiple modules. This release delivers jkit-sql, jkit-sql-auto (with Spring Boot 2 / 3 starters), jkit-notify, jkit-notify-extra, and jkit-curl-codegen alongside the parent POM. The coordinate com.alianga:jkit still refers to jkit-core.
Added
jkit-sql
- New module
com.alianga:jkit-sql: zero-dependency hand-written SQL parser (lexical analysis viachar[]+ open-address keyword hashing, recursive-descent AST). Entry points:SQL.parse/parseAll/parseExpr/format/toSqlString/tables/stat/parameters/parameterize/exportParameterValues/wall/clone/eval/rewrite. Invalid SQL throwsSqlParseException. Usage: seedocs/sql.md. - Dialects: first-class enum
MYSQL(default),POSTGRES,ORACLE(pre-12c ROWNUM pagination),ORACLE12(OFFSET/FETCH),SQLSERVER,ANSI,H2,DB2,SQLITE,HIVE(aliasesmaxcompute/odps/argo),CLICKHOUSE,PRESTO(aliastrino),DAMENG(dm/dameng/jdbc:dm:).fromNamecovers GoldenDB / SelectDB / AnalyticDB / MatrixOne / StoneDB / HighGo / UXDB / MogDB / Vastbase / AntDB / IvorySQL / GBase 8a / GBase 8s / Xingyun / Oscar and more, aligning with icellcommon-modelSQL data sources. Capabilities are driven bySqlDialectSpecand can be individually overridden viaSqlDialectWrapper(e.g. MySQLANSI_QUOTES). - Pagination:
getLimit/getOffset/setLimit/setOffset/setPage/addLimit/SQL.adaptPagination.format/toSqlStringadapt to the target dialect (MySQLLIMIT↔ classic ORACLE ROWNUM ↔ ORACLE12OFFSET FETCH↔ SQL ServerTOP|OFFSET FETCH).SqlBuilder.limit/offsetgenerate output based on the active dialect instead of always emittingLIMIT. - Rewrite chain:
SqlRewriteHook+SqlRewrites(addLimit/setPage/andWhere/replaceTable/replaceColumn/adaptPagination/addSelectItem/removeSelectItem, combinable with custom rules) +SQL.rewrite(deep-clone then mutate). Cross-dialect function rewriting covers JOIN ON / MERGE / OVER / columnDEFAULT:DATE_ADD/DATEDIFF/FROM_UNIXTIME/UNIX_TIMESTAMP/SUBSTRING/LEFT/RIGHT/DECODE/NVL2/UCASE/LCASE/CONCAT_WS/LPAD/RPAD/YEAR/MONTH/DAY/HOUR/MINUTE/SECOND/SYSDATE/LAST_DAY/CHAR/CHRetc.; FULLTEXT upgraded toMANUAL_ACTION_REQUIRED. Function rewriting is extensible viaSqlSchemaConverterProvider.registerFunctions(SPI override). - Entity DDL:
SqlEntitiesscans@SqlTable/ JPA / MyBatis-Plus / MyBatis@Alias(no third-party compile dependencies). Public APIs:columnSql/columnTypeSql/createIndex/orderByForeignKeys/extraSql/sequenceSql/indexName/createTable(..., includeIndexes). Recognizes any simple-nameCommentannotation and Hibernate@ColumnDefault;@SqlTable(comment)/@SqlColumn(comment)generate dialect-aware comments; Oracle ≤11g appends SEQUENCE + TRIGGER when IDENTITY is unavailable. - Formatting & parse options:
SqlFormatOptions.keywordCase(AS_IS/UPPER/LOWER),quoteIdentifiers;SqlParseOptions.pipesAsConcat/keepComments/placeholders()(template placeholders) /statementParsers()(custom statement parsers keyed by leading keyword). - Security & extensibility:
SQL.wall→SqlWallResult;SqlWallRulechain +SqlWallConfig.rules(...)(built-in selectOnly / denyDdl / no-WHERE writes etc., behavior and violation codes unchanged).SqlAstVisitortype dispatch (non-breaking toSqlVisitorAdapter). - Statement AST:
EXPLAIN/SET/COMMENT ON/SHOW/ANALYZE|VACUUM|OPTIMIZE/ transaction control /COPY/FLUSH/START TRANSACTION/LOAD DATA/LOCK TABLES/PREPARE/BEGIN…END/ routines·triggers·events (SqlRoutineParam/SqlControlStatement/SqlDeclareStatement/SqlHandlerStatement) promoted to standalone types;MODEL/MATCH_RECOGNIZEstructured;RENAME TABLEindependent statement. - SqlBuilder: fluent SELECT / INSERT / UPDATE / DELETE,
join/leftJoin/rightJoin/fullJoin/crossJoin/union/with/distinct/groupBy/having. - Parsing coverage: relative to Druid BVT / JSqlParser inline / file corpora approximately 96.3% / 92.3% / 90.0%. Window functions, CTE, MERGE, PIVOT, flashback, procedure blocks, Hive / ClickHouse / Informix / DB2 edge-case syntax and more are documented in
docs/sql.mdrather than listed per iteration.
jkit-sql-auto
- New module
com.alianga:jkit-sql-auto: auto-creates / updates tables at startup based on entities. Scans@SqlTable/ JPA / MyBatis-Plus, compares againstDatabaseMetaData, and executesCREATE TABLE/ALTER TABLE ADD/CREATE INDEX. Modes:none/validate/update(default, append only) /create/create-drop. Configuration prefixjkit.sql.auto.*, data source falls back tospring.datasource.*. Zero third-party runtime dependencies. SqlAuto.dropdeletes managed tables in reverse foreign-key order; Spring Boot 2 / 3 auto-configuration modulesjkit-sql-auto-spring-boot-2andjkit-sql-auto-spring-boot-3.- Table / column comments executed per dialect (
COMMENT/COMMENT ON/sp_addextendedproperty); Oracle ≤11g auto-increment primary keys use SEQUENCE + TRIGGER; Dameng uses IDENTITY on columns. postgresIdentityStyle/foreignKeys/autoIncrement: disable the corresponding DDL for products with limited capabilities such as OpenGauss / GBase 8a / DuckDB.table-prefix(jkit.sql.auto.table-prefix/ chaintablePrefix(String)): prepends a prefix (e.g.t_) to all auto-generated table names; applies to create / alter / drop / indexes / sequences / foreign-key target tables.index-prefix-enabled(jkit.sql.auto.index-prefix-enabled, defaulttrue): whether auto-derived index names also carry the table prefix; explicit@Index(name=…)on entities is always preserved as-is.
jkit-notify
- New module
com.alianga:jkit-notify: DingTalk (with signing), WeCom, Feishu (with signature), ServerChan, Bark, generic Webhook, SMTP (raw socket: AUTH LOGIN + STARTTLS/SSL + MIME).NotificationChannelSPI +NotificationManager;MessageType(TEXT / MARKDOWN / HTML). Zero third-party dependencies. Usage: seedocs/notify.md. - Failure classification:
SendResult.failureType()/isRetryable(),FailureTypeis RETRYABLE / THROTTLED / CONFIG_ERROR / PERMANENT / SUPPRESSED; unlisted error codes fall back to the HTTP status. Custom channels can overrideAbstractHttpChannel.classify. - Messages truncated by UTF-8 bytes (no splitting of CJK characters or emoji); DingTalk @-mentions automatically append missing
@phoneinto the body;Message.var/varsreplace${key}/${a.b}; attachments streamed, split at10MB/512KB, MIME auto-detected. - SMTP:
sslProtocolspins the TLS version,trustAllCertssupports self-signed gateways; MARKDOWN sent astext/htmlviaNotifyUtils.markdownToHtml. Feishu signature written into the JSON request body.NotifyPolicy: quiet hours, 5-minute deduplication, local rate limiting;sendFailoversequential account failover. - Optional module
com.alianga:jkit-notify-extra: Slack / Telegram / ntfy / Alibaba Cloud / Tencent Cloud / Yunpian / Huawei Cloud SMS. Channel extras live on their implementation classes, not in the coreMessage.
jkit-core
DateUtils.parse(String): auto-detects timestamps, compact numerics,-//., Chinese / Korean formats, ISO-8601 (includingT/Z/+0800/+08:00).DateUtils.fromEpochNumber(long): converts 10-digit seconds or 13-digit milliseconds toDate.DateUtils.fromTemporal(TemporalAccessor): convertsjava.timeobjects toDate.ConvertUtils.toDateadditionally supportsInstant,OffsetDateTime,ZonedDateTime.HttpUtils.debug/HttpUtils.printCurl: logs the request summary or equivalent curl to stdout before sending (do not enable in production).ConfigLoadOptions.addLocation: appends directories to the default search path (classpath:/file:/ bare path /File,~expanded touser.home).Print.enableLog: whenfalse, prints to console only without writing to JUL.com.alianga.jkit.log.LocaleFormatter: JUL formatter with a fixed Locale.
jkit-curl-codegen
- Extracted from jkit as a standalone artifact
com.alianga:jkit-curl-codegen, entry pointCurlCodegen.generate(id, curl). Covers 34 targets: Java (jkit / JDK 11+ / OkHttp / Apache 5 / HttpURLConnection / Unirest), Kotlin, JavaScript (fetch / axios / request / jQuery / XHR etc.), Python (requests / httpx), Go, C#, PHP, R, Rust, Swift, Ruby, Lua, PowerShell, curl (Windows cmd / PowerShell), wget, plushttp/har/httpieinterop formats.
Changed
jkit-sql: Breaking —SQL.andWhere/replaceTable/replaceColumnnow use the same clone-then-mutate pattern asaddLimit/setPage(return a new AST, leave the original tree untouched); callers must use the return value.jkit-sql:SQL.clonenow performs a true AST tree copy (SqlAstCloner/SqlNode.copy); hot paths no longer format→parse; cross-dialectformat/adaptPagination/setPagesignificantly faster. Classic ORACLE offset=0 single-layer ROWNUM wrapping skips cloning.jkit-sql-auto:dryRun(true)onSqlAuto.run(options)/plan(options)no longer opens a JDBC connection / DataSource. The URL is only used to infer the dialect; a fullCREATE TABLEplan is produced against a blank database. Overloads that receive aConnectionstill compare against live tables but do not execute.jkit-core:DateUtilsdropsSimpleDateFormat.yyyy-MM-dd HH:mm:ssuseschar[]+ second-level cache; other common patterns use hand-written assembly orDateTimeFormatter.ConvertUtils.toDate(Object)refactored to extract by numeric field. Common string parsing roughly an order of magnitude faster; results aligned with ZmlTools.jkit-core: JUL default format changed to English level names (WARNING/SEVERE), no longer following the JVM default locale.jkit-core:RandomUtils.randomBirthswitched toLocalDate(no longer throws whenminAge == maxAge);getUUIDdash removal no longer uses regex;IdCardUtilsrandom birthday generates by actual days in the month.jkit-core: curl parsing aligned with curl semantics (expands-kLs/-XPOST,--json/--data-urlencode,@fileno longer treated as literal body). Public APIs:parseCurl/curlToRequest/curl/curlString/requestToCurl, modelParsedCurlRequest.CurlParser.generateand thehttp.codegenpackage removed from jkit core; depend onjkit-curl-codegeninstead.jkit-notify: coreChannelConfigremoves SMS-specifictemplate/appId/region; replaced byChannelConfig.extra+AbstractSmsChannel.CFG_*. Core channels registered only viadefaults(); SPI loads only extra modules to avoid double registration.jkit-notify-extra:SlackChannel.EXTRA_CHANNELreplaces usingMessage.EXTRA_GROUPfor Slack channels (brief backward-compatible fallback toEXTRA_GROUP).
Fixed
jkit-core: when the response declaresContent-Encoding: gzipbut the body is empty, JDK 8 no longer throwsEOFException; returns an empty stream (underlying connection is still reclaimed on close).jkit-sql: when a subclass and aMappedSuperclass/ parent class both declare a same-named column, only the subclass field is kept, preventing PostgreSQLcolumn specified more than once.jkit-sql:@GeneratedValue(generator="system-uuid")/GenerationType.UUID/ non-integer@SqlGeneratedno longer produceAUTO_INCREMENT/IDENTITY(PostgreSQL would syntax-error onIDENTITYfor aVARCHARprimary key).jkit-sql: auto-generated index / sequence / trigger names are truncated to the dialect identifier length limit (classic Oracle 30 characters; when exceeded, the prefix is kept plus a 4-character hash). Unnamed indexes changed to{table}_{col}_idx; multiple unnamed indexes on the same table no longer collide. Public APIs:SqlDialectSpec.maxIdentifierLength/fitIdentifier.jkit-sql:SQL.formatpretty mode now wraps and indentsCREATE TABLEcolumns (toSqlString/ compact still single-line).jkit-sql:GROUP_CONCATwithout an explicitSEPARATORno longer loses the separator when rewriting toSTRING_AGG/LISTAGG; MySQLMODIFY/CHANGErewritten to PG/ANSI/H2/PRESTO no longer duplicate the column name.jkit-sql:SUBSTRING/LEFT/RIGHTrewritten per dialect (PG/MySQL/H2 useFROM n FOR m; SQL Server/SQLite/Hive/ClickHouse use comma form; negative start rewritten withLENGTH/LEN); Dameng bare SELECT appendsFROM dual.jkit-sql:SqlDialectWrapperno longer delegates derived methods (identQuoteClose/quoteIdent/pipesAreConcat/preferredLimitStyle); subclass overrides of primitives now automatically cascade to derived capabilities.jkit-sql: rewrite fidelity —NATURAL JOINno longer loses its modifier;RENAME TABLEno longer writes illegalALTER TABLE;ADD UNIQUE KEYno longer dropsUNIQUE;IGNORE/LOW_PRIORITY/HIGH_PRIORITYonINSERT/UPDATE/DELETEare preserved in the AST and rewritten; non-bare-identifier aliases are force-quoted.jkit-sql:tables()now capturesCREATE TRIGGER/CREATE TABLE … LIKE/RENAME TO/ multi-tableOPTIMIZE|ANALYZE; database names, routine names, and CTE names are no longer miscounted. Comment-only / blank-only input is classified asOTHERinstead of throwingempty SQL.jkit-sql-auto: on classic Oracle, auto-generatedCREATE INDEXnames exceeding 30 characters are now truncated to avoid ORA-00972.jkit-curl-codegen: R (httr2) now usesreq_perform(); PowerShell 5.1 restricted headers / cookie commas / Chinese encoding fixed; Windows curl split into cmd (shell-curl-windows) and PowerShell (shell-curl-powershell) generators.
Build
- Repository converted to multi-module: parent POM
com.alianga:jkit-parent, runtime library injkit-core(release coordinate remainscom.alianga:jkit), other capabilities in separate modules.mvn testat the root builds all modules. git-commit-id-plugin,buildnumber-maven-plugin,maven-source-pluginmoved from thepublishprofile to the default build;packagenow produces sources jars with build info.- The full reactor can be built with JDK 8 launching Maven: other modules compile against JDK 8;
jkit-core'sMETA-INF/versions/9,/11use toolchains targeting JDK 9 / 11; onlyjkit-sql-auto-spring-boot-3uses toolchain for JDK 17 (required by Spring Boot 3). Requires~/.m2/toolchains.xmldeclaring jdk 8/9/11/17/21. Launch example:JAVA_HOME=$(jdk8) mvn -o clean install. jkit-notifylive-use tests require-Djkit.notify.live=trueand complete YML credentials; defaultmvn testnever sends live messages even when keys are present.
Documentation
docs/sql.md/docs/en/sql.mdupdated to align with code: cross-dialect pagination,SQL.clone,SqlRewrites,SqlEntitiespublic methods, and dialect tables; API coverage is now aligned on both sides. READMEs and module READMEs removed internal development-plan links.docs/sql-auto.md(and its English version) reordered to follow "pick package → write entity → Spring Boot / non-Spring → configuration" flow; module READMEs and quick-start pages synchronized.
2.0.0
jkit's first public release, migrated from ZmlTools: zero third-party dependencies, package renamed to com.alianga.jkit, coordinate com.alianga:jkit:2.0.0.
Capability summary in README.md: CSV / HTTP / JSON / YAML / configuration / expressions etc. all rewritten with pure JDK. Projects migrating from ZmlTools can continue using relocated/zmltools to redirect top.wuyongshi:ZmlTools:2.0.0 to this coordinate (package names still require manual replacement).
How to Record Future Versions
- Change the
<version>in the rootpom.xml(jkit-parent) to the new version number; child modules inherit it. Update the README header and dependency examples, and the version printed byMain.java. - Insert the new version above the current latest-version section, with today's date (
YYYY-MM-DD). Each version number gets exactly one H2 heading; before release, append new items to that section — do not open a separate## x.y.z / unreleased. - Document caller-visible changes under Added / Changed / Fixed / Build (use bold module names for grouping in multi-module projects). Do not simply paste git titles, and do not record internal milestones or competitor-coverage rounds (those go in
docs/sql.md/docs/next-plan.md). - Add
@since x.y.zto javadoc for newly added public APIs. - Leave historical versions in place; do not rewrite old entries as new versions.
Template:
## x.y.z - YYYY-MM-DD
### Added
- ...
### Changed
- ...
### Fixed
- ...
### Build
- ...Omit any section heading when there are no changes in that category.