diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6afff4a3c..9a6601673 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,7 @@
-------------------------------------------------------------------------------------------------------------
-# 6.0.0-M10 (2023-12-28)
+# 6.0.0-M11 (2024-01-11)
### 计划实现
* 【poi 】 Markdown相关(如HTML转换等),基于commonmark-java
diff --git a/README-EN.md b/README-EN.md
index c71a5d51f..20a0901ae 100755
--- a/README-EN.md
+++ b/README-EN.md
@@ -144,18 +144,18 @@ We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop:
org.dromara.hutool
hutool-all
- 6.0.0-M10
+ 6.0.0-M11
```
### 🍐Gradle
```
-implementation 'org.dromara.hutool:hutool-all:6.0.0-M10'
+implementation 'org.dromara.hutool:hutool-all:6.0.0-M11'
```
## 📥Download
-- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/6.0.0-M10/)
+- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/6.0.0-M11/)
> 🔔️note:
> Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.
diff --git a/README.md b/README.md
index b2cc82c74..f65dccc66 100755
--- a/README.md
+++ b/README.md
@@ -139,21 +139,21 @@
org.dromara.hutool
hutool-all
- 6.0.0-M10
+ 6.0.0-M11
```
### 🍐Gradle
```
-implementation 'org.dromara.hutool:hutool-all:6.0.0-M10'
+implementation 'org.dromara.hutool:hutool-all:6.0.0-M11'
```
### 📥下载jar
点击以下链接,下载`hutool-all-X.X.X.jar`即可:
-- [Maven中央库](https://repo1.maven.org/maven2/org/dromara/hutool/hutool-all/6.0.0-M10/)
+- [Maven中央库](https://repo1.maven.org/maven2/org/dromara/hutool/hutool-all/6.0.0-M11/)
> 🔔️注意
> Hutool 6.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
diff --git a/bin/version.txt b/bin/version.txt
index 76f9249ad..bdfaba25f 100644
--- a/bin/version.txt
+++ b/bin/version.txt
@@ -1 +1 @@
-6.0.0-M10
+6.0.0-M11
diff --git a/docs/js/version.js b/docs/js/version.js
index 2221a2e15..afee3da9a 100755
--- a/docs/js/version.js
+++ b/docs/js/version.js
@@ -1 +1 @@
-var version = '6.0.0-M10'
\ No newline at end of file
+var version = '6.0.0-M11'
\ No newline at end of file
diff --git a/hutool-all/pom.xml b/hutool-all/pom.xml
index d7a896e56..1317ca64f 100755
--- a/hutool-all/pom.xml
+++ b/hutool-all/pom.xml
@@ -21,7 +21,7 @@
org.dromara.hutool
hutool-parent
- 6.0.0-M10
+ 6.0.0-M11
hutool-all
diff --git a/hutool-bom/pom.xml b/hutool-bom/pom.xml
index 231002101..411a1ff45 100755
--- a/hutool-bom/pom.xml
+++ b/hutool-bom/pom.xml
@@ -21,7 +21,7 @@
org.dromara.hutool
hutool-parent
- 6.0.0-M10
+ 6.0.0-M11
hutool-bom
diff --git a/hutool-core/pom.xml b/hutool-core/pom.xml
index f83eb80fb..8c247ce36 100755
--- a/hutool-core/pom.xml
+++ b/hutool-core/pom.xml
@@ -21,7 +21,7 @@
org.dromara.hutool
hutool-parent
- 6.0.0-M10
+ 6.0.0-M11
hutool-core
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipUtil.java
index 1afb561c8..a947041ac 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipUtil.java
@@ -33,6 +33,7 @@ import org.dromara.hutool.core.util.ObjUtil;
import java.io.*;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.*;
import java.util.ArrayList;
@@ -726,7 +727,7 @@ public class ZipUtil {
* Gzip压缩处理
*
* @param content 被压缩的字符串
- * @param charset 编码
+ * @param charset 编码 {@link StandardCharsets#UTF_8}、 {@link CharsetUtil#UTF_8}
* @return 压缩后的字节流
* @throws HutoolException IO异常
*/
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/lang/wrapper/SimpleWrapper.java b/hutool-core/src/main/java/org/dromara/hutool/core/lang/wrapper/SimpleWrapper.java
index c67ba52e7..a1d199af2 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/lang/wrapper/SimpleWrapper.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/lang/wrapper/SimpleWrapper.java
@@ -13,7 +13,8 @@
package org.dromara.hutool.core.lang.wrapper;
/**
- * 简单包装对象
+ * 简单包装对象
+ * 通过继承此类,可以直接使用被包装的对象,用于简化和统一封装。
*
* @param 被包装对象类型
* @author looly
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberParser.java
index 9038c4685..3c8779b23 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberParser.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberParser.java
@@ -297,6 +297,7 @@ public class NumberParser {
// issue#I79VS7
numberStr = StrUtil.subSuf(numberStr, 1);
}
+
try {
final NumberFormat format = NumberFormat.getInstance(locale);
if (format instanceof DecimalFormat) {
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java
index e81fd333e..561a86959 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/math/NumberUtil.java
@@ -898,12 +898,12 @@ public class NumberUtil extends NumberValidator {
}
// Float、Double等有精度问题,转换为字符串后再转换
- return toBigDecimal(number.toString());
+ return new BigDecimal(number.toString());
}
/**
* 数字转{@link BigDecimal}
- * null或""或空白符转换为0
+ * null或""或"NaN"或空白符转换为0
*
* @param numberStr 数字字符串
* @return {@link BigDecimal}
@@ -927,7 +927,7 @@ public class NumberUtil extends NumberValidator {
/**
* 数字转{@link BigInteger}
- * null转换为0
+ * null或"NaN"转换为0
*
* @param number 数字
* @return {@link BigInteger}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/StrUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/StrUtil.java
index fb52e0b62..ebb26bae8 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/text/StrUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/StrUtil.java
@@ -15,6 +15,7 @@ package org.dromara.hutool.core.text;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.func.FunctionPool;
import org.dromara.hutool.core.text.placeholder.StrFormatter;
+import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.core.util.CharsetUtil;
import java.io.StringReader;
@@ -27,6 +28,13 @@ import java.util.Map;
* 字符串工具类
* 此工具主要针对单个字符串的操作
*
+ * 本工具类,v6.x进行了拆分。
+ * 字符串分割split参考:{@link SplitUtil}
+ * 多字符串判空hasBlank参考:{@link ArrayUtil}
+ *
+ * @see SplitUtil#split(CharSequence, CharSequence) 对字符串分割
+ * @see ArrayUtil#hasBlank(CharSequence...) 对多个字符串判空
+ *
* @author Looly
*/
public class StrUtil extends CharSequenceUtil implements StrPool {
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlConstants.java b/hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlConstants.java
index 01977d2ed..b77d2af83 100755
--- a/hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlConstants.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/xml/XmlConstants.java
@@ -12,6 +12,8 @@
package org.dromara.hutool.core.xml;
+import org.dromara.hutool.core.text.CharUtil;
+
import java.util.regex.Pattern;
/**
@@ -30,6 +32,10 @@ public class XmlConstants {
* 字符串常量:XML And 符转义 {@code "&" -> "&"}
*/
public static final String AMP = "&";
+ /**
+ * The Character '&'.
+ */
+ public static final Character C_AMP = CharUtil.AMP;
/**
* 字符串常量:XML 双引号转义 {@code """ -> "\""}
@@ -40,17 +46,41 @@ public class XmlConstants {
* 字符串常量:XML 单引号转义 {@code "&apos" -> "'"}
*/
public static final String APOS = "'";
+ /**
+ * The Character '''.
+ */
+ public static final Character C_APOS = CharUtil.SINGLE_QUOTE;
/**
* 字符串常量:XML 小于号转义 {@code "<" -> "<"}
*/
public static final String LT = "<";
+ /**
+ * The Character '<'.
+ */
+ public static final Character C_LT = '<';
+
/**
* 字符串常量:XML 大于号转义 {@code ">" -> ">"}
*/
public static final String GT = ">";
+ /**
+ * The Character '>'.
+ */
+ public static final Character C_GT = '>';
+
+ /**
+ * The Character '!'.
+ */
+ public static final Character C_BANG = '!';
+
+ /**
+ * The Character '?'.
+ */
+ public static final Character C_QUEST = '?';
+
/**
* 在XML中无效的字符 正则
*/
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/math/Issue3423Test.java b/hutool-core/src/test/java/org/dromara/hutool/core/math/Issue3423Test.java
new file mode 100644
index 000000000..9579a300f
--- /dev/null
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/math/Issue3423Test.java
@@ -0,0 +1,28 @@
+package org.dromara.hutool.core.math;
+
+import org.dromara.hutool.core.lang.Console;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.ParseException;
+
+public class Issue3423Test {
+ @Test
+ public void toBigDecimalOfNaNTest() {
+ final BigDecimal naN = NumberUtil.toBigDecimal("NaN");
+ Assertions.assertEquals(BigDecimal.ZERO, naN);
+ }
+
+ @Test
+ @Disabled
+ public void toBigDecimalOfNaNTest2() throws ParseException {
+ final NumberFormat format = NumberFormat.getInstance();
+ ((DecimalFormat) format).setParseBigDecimal(true);
+ final Number naN = format.parse("NaN");
+ Console.log(naN.getClass());
+ }
+}
diff --git a/hutool-cron/pom.xml b/hutool-cron/pom.xml
index d7a4bd4f7..4c4f7350a 100755
--- a/hutool-cron/pom.xml
+++ b/hutool-cron/pom.xml
@@ -21,7 +21,7 @@
org.dromara.hutool
hutool-parent
- 6.0.0-M10
+ 6.0.0-M11
hutool-cron
diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/CronConfig.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/CronConfig.java
index 47fbe1a6b..1df148ebf 100644
--- a/hutool-cron/src/main/java/org/dromara/hutool/cron/CronConfig.java
+++ b/hutool-cron/src/main/java/org/dromara/hutool/cron/CronConfig.java
@@ -31,6 +31,9 @@ public class CronConfig {
*/
protected boolean matchSecond;
+ /**
+ * 构造
+ */
public CronConfig(){
}
diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/CronTimer.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/CronTimer.java
index 085f01818..3ad60d746 100644
--- a/hutool-cron/src/main/java/org/dromara/hutool/cron/CronTimer.java
+++ b/hutool-cron/src/main/java/org/dromara/hutool/cron/CronTimer.java
@@ -13,6 +13,7 @@
package org.dromara.hutool.cron;
import org.dromara.hutool.core.date.DateUnit;
+import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.thread.ThreadUtil;
import org.dromara.hutool.log.Log;
@@ -63,9 +64,14 @@ public class CronTimer extends Thread implements Serializable {
//等待直到下一个时间点,如果被中断直接退出Timer
break;
}
+
//执行点,时间记录为执行开始的时间,而非结束时间
- thisTime = System.currentTimeMillis();
- spawnLauncher(thisTime);
+ spawnLauncher(nextTime);
+
+ // issue#3460 采用叠加方式,确保正好是1分钟或1秒,避免sleep晚醒问题
+ // 此处无需校验,因为每次循环都是sleep与上触发点的时间差。
+ // 当上一次晚醒后,本次会减少sleep时间,保证误差在一个unit内,并不断修正。
+ thisTime = nextTime;
} else{
// 非正常时间重新计算(issue#1224@Github)
thisTime = System.currentTimeMillis();
diff --git a/hutool-cron/src/test/java/org/dromara/hutool/cron/demo/DeamonMainTest.java b/hutool-cron/src/test/java/org/dromara/hutool/cron/demo/DeamonMainTest.java
index c7f982aff..40188baed 100644
--- a/hutool-cron/src/test/java/org/dromara/hutool/cron/demo/DeamonMainTest.java
+++ b/hutool-cron/src/test/java/org/dromara/hutool/cron/demo/DeamonMainTest.java
@@ -19,7 +19,7 @@ import org.dromara.hutool.cron.task.InvokeTask;
public class DeamonMainTest {
public static void main(final String[] args) {
// 测试守护线程是否对作业线程有效
- CronUtil.schedule("*/2 * * * * *", new InvokeTask("demo.org.dromara.hutool.cron.TestJob.doWhileTest"));
+ CronUtil.schedule("*/2 * * * * *", new InvokeTask("org.dromara.hutool.cron.demo.TestJob.doWhileTest"));
// 当为守护线程时,stop方法调用后doWhileTest里的循环输出将终止,表示作业线程正常结束
// 当非守护线程时,stop方法调用后,不再产生新的作业,原作业正常执行。
CronUtil.setMatchSecond(true);
diff --git a/hutool-cron/src/test/java/org/dromara/hutool/cron/demo/TestJob2.java b/hutool-cron/src/test/java/org/dromara/hutool/cron/demo/TestJob2.java
index 00a7e20d7..b3baf4197 100644
--- a/hutool-cron/src/test/java/org/dromara/hutool/cron/demo/TestJob2.java
+++ b/hutool-cron/src/test/java/org/dromara/hutool/cron/demo/TestJob2.java
@@ -14,6 +14,7 @@ package org.dromara.hutool.cron.demo;
import java.util.concurrent.TimeUnit;
+import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.thread.ThreadUtil;
@@ -29,7 +30,7 @@ public class TestJob2 {
* 执行定时任务内容
*/
public void doTest() {
- Console.log("TestJob2.doTest开始执行……");
+ Console.log("TestJob2.doTest开始执行…… at [{}]", DateUtil.formatNow());
ThreadUtil.sleep(20, TimeUnit.SECONDS);
Console.log("延迟20s打印testJob2");
}
diff --git a/hutool-cron/src/test/resources/config/cron.setting b/hutool-cron/src/test/resources/config/cron.setting
index 4e2860de9..62faca9fc 100644
--- a/hutool-cron/src/test/resources/config/cron.setting
+++ b/hutool-cron/src/test/resources/config/cron.setting
@@ -8,7 +8,7 @@
# demo.org.dromara.hutool.cron.TestJob.doTest = */1 * * * * *
-[org.dromara.hutool.cron.demo]=
+[org.dromara.hutool.cron.demo]
# 6位表达式在秒匹配模式下可用,此处表示每秒执行一次
# TestJob.doTest = */1 * * * * *
# 5位表达式在分匹配模式下可用,此处表示每分钟执行一次
diff --git a/hutool-crypto/pom.xml b/hutool-crypto/pom.xml
index 0428e92fa..e82c73ae0 100755
--- a/hutool-crypto/pom.xml
+++ b/hutool-crypto/pom.xml
@@ -21,7 +21,7 @@
org.dromara.hutool
hutool-parent
- 6.0.0-M10
+ 6.0.0-M11
hutool-crypto
@@ -31,7 +31,7 @@
org.dromara.hutool.crypto
- 1.76
+ 1.77
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Cipher.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Cipher.java
new file mode 100644
index 000000000..6709cfb84
--- /dev/null
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Cipher.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2024. looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * https://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.crypto;
+
+import org.bouncycastle.crypto.BufferedBlockCipher;
+
+import java.util.Arrays;
+
+/**
+ * 密码接口,提供统一的API,用于兼容和统一JCE和BouncyCastle等库的操作
+ *
+ * @author Looly
+ * @since 6.0.0
+ */
+public interface Cipher {
+
+ /**
+ * 获取算法名称
+ *
+ * @return 算法名称
+ */
+ String getAlgorithmName();
+
+ /**
+ * 获取块大小,当为Stream方式加密时返回0
+ *
+ * @return 块大小,-1表示非块加密
+ */
+ int getBlockSize();
+
+ /**
+ * 初始化模式和参数
+ *
+ * @param mode 模式,如加密模式或解密模式
+ * @param parameters Cipher所需参数,包括Key、Random、IV等信息
+ */
+ void init(CipherMode mode, Parameters parameters);
+
+ /**
+ * 根据输入长度,获取输出长度,输出长度与算法相关
+ * 输出长度只针对本次输入关联,即len长度的数据对应输出长度加doFinal的长度
+ *
+ * @param len 输入长度
+ * @return 输出长度,-1表示非块加密
+ */
+ int getOutputSize(int len);
+
+ /**
+ * 执行运算,可以是加密运算或解密运算
+ *
+ * @param in 输入数据
+ * @param inOff 输入数据开始位置
+ * @param len 被处理数据长度
+ * @param out 输出数据
+ * @param outOff 输出数据开始位置
+ * @return 处理长度
+ */
+ int process(byte[] in, int inOff, int len, byte[] out, int outOff);
+
+ /**
+ * 处理最后一块数据
+ * 当{@link #process(byte[], int, int, byte[], int)}处理完数据后非完整块数据,此方法用于处理块中剩余的bytes
+ * 如加密数据要求128bit,即16byes的整数,单数处理数据后为15bytes,此时根据padding方式不同,可填充剩余1byte为指定值(如填充0)
+ * 当对数据进行分段加密时,需要首先多次执行process方法,在最后一块数据处理完后执行此方法。
+ *
+ * @param out 经过process执行过运算的结果数据
+ * @param outOff 数据处理开始位置
+ * @return 处理长度
+ */
+ int doFinal(byte[] out, int outOff);
+
+ /**
+ * 处理数据,并返回最终结果
+ *
+ * @param in 输入数据
+ * @return 结果数据
+ */
+ default byte[] processFinal(final byte[] in) {
+ final byte[] buf = new byte[getOutputSize(in.length)];
+ int len = process(in, 0, in.length, buf, 0);
+ len += doFinal(buf, len);
+
+ if (len == buf.length) {
+ return buf;
+ }
+ return Arrays.copyOfRange(buf, 0, len);
+ }
+
+ /**
+ * Cipher所需参数,包括Key、Random、IV等信息
+ */
+ interface Parameters {
+ }
+}
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/CipherMode.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/CipherMode.java
index c94eb2336..4e2317261 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/CipherMode.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/CipherMode.java
@@ -24,19 +24,19 @@ public enum CipherMode {
/**
* 加密模式
*/
- encrypt(Cipher.ENCRYPT_MODE),
+ ENCRYPT(Cipher.ENCRYPT_MODE),
/**
* 解密模式
*/
- decrypt(Cipher.DECRYPT_MODE),
+ DECRYPT(Cipher.DECRYPT_MODE),
/**
* 包装模式
*/
- wrap(Cipher.WRAP_MODE),
+ WRAP(Cipher.WRAP_MODE),
/**
* 拆包模式
*/
- unwrap(Cipher.UNWRAP_MODE);
+ UNWRAP(Cipher.UNWRAP_MODE);
/**
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/CipherWrapper.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/CipherWrapper.java
deleted file mode 100644
index 7a4077932..000000000
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/CipherWrapper.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (c) 2023 looly(loolly@aliyun.com)
- * Hutool is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- * https://license.coscl.org.cn/MulanPSL2
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- */
-
-package org.dromara.hutool.crypto;
-
-import org.dromara.hutool.core.lang.wrapper.Wrapper;
-
-import javax.crypto.Cipher;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
-
-/**
- * {@link Cipher}包装类,提供初始化模式等额外方法
- * 包装之后可提供自定义或默认的:
- *
- * - {@link AlgorithmParameterSpec}
- * - {@link SecureRandom}
- *
- *
- * @author looly
- * @since 5.7.17
- */
-public class CipherWrapper implements Wrapper {
-
- private final Cipher cipher;
- /**
- * 算法参数
- */
- private AlgorithmParameterSpec params;
- /**
- * 随机数生成器,可自定义随机数种子
- */
- private SecureRandom random;
-
- /**
- * 构造
- *
- * @param algorithm 算法名称
- */
- public CipherWrapper(final String algorithm) {
- this(SecureUtil.createCipher(algorithm));
- }
-
- /**
- * 构造
- *
- * @param cipher {@link Cipher}
- */
- public CipherWrapper(final Cipher cipher) {
- this.cipher = cipher;
- }
-
- /**
- * 获取{@link AlgorithmParameterSpec}
- * 在某些算法中,需要特别的参数,例如在ECIES中,此处为IESParameterSpec
- *
- * @return {@link AlgorithmParameterSpec}
- */
- public AlgorithmParameterSpec getParams() {
- return this.params;
- }
-
- /**
- * 设置 {@link AlgorithmParameterSpec},通常用于加盐或偏移向量
- *
- * @param params {@link AlgorithmParameterSpec}
- * @return this
- */
- public CipherWrapper setParams(final AlgorithmParameterSpec params) {
- this.params = params;
- return this;
- }
-
- /**
- * 设置随机数生成器,可自定义随机数种子
- *
- * @param random 随机数生成器,可自定义随机数种子
- * @return this
- */
- public CipherWrapper setRandom(final SecureRandom random) {
- this.random = random;
- return this;
- }
-
- /**
- * 获取被包装的{@link Cipher}
- *
- * @return {@link Cipher}
- */
- @Override
- public Cipher getRaw() {
- return this.cipher;
- }
-
- /**
- * 初始化{@link Cipher}为加密或者解密模式
- *
- * @param mode 模式,见{@link Cipher#ENCRYPT_MODE} 或 {@link Cipher#DECRYPT_MODE}
- * @param key 密钥
- * @return this
- * @throws InvalidKeyException 无效key
- * @throws InvalidAlgorithmParameterException 无效算法
- */
- public CipherWrapper initMode(final int mode, final Key key)
- throws InvalidKeyException, InvalidAlgorithmParameterException {
- final Cipher cipher = this.cipher;
- final AlgorithmParameterSpec params = this.params;
- final SecureRandom random = this.random;
- if (null != params) {
- if (null != random) {
- cipher.init(mode, key, params, random);
- } else {
- cipher.init(mode, key, params);
- }
- } else {
- if (null != random) {
- cipher.init(mode, key, random);
- } else {
- cipher.init(mode, key);
- }
- }
- return this;
- }
-}
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/JceCipher.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/JceCipher.java
new file mode 100644
index 000000000..11625d35c
--- /dev/null
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/JceCipher.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2024. looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * https://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.crypto;
+
+import org.dromara.hutool.core.lang.Assert;
+import org.dromara.hutool.core.lang.wrapper.SimpleWrapper;
+
+import javax.crypto.ShortBufferException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+/**
+ * 提供{@link javax.crypto.Cipher}的方法包装
+ *
+ * @author Looly
+ */
+public class JceCipher extends SimpleWrapper implements Cipher {
+
+ /**
+ * 构造
+ *
+ * @param algorithm 算法名称
+ */
+ public JceCipher(final String algorithm) {
+ this(SecureUtil.createCipher(algorithm));
+ }
+
+ /**
+ * 构造
+ *
+ * @param cipher {@link javax.crypto.Cipher},可以通过{@link javax.crypto.Cipher#getInstance(String)}创建
+ */
+ public JceCipher(final javax.crypto.Cipher cipher) {
+ super(Assert.notNull(cipher));
+ }
+
+ @Override
+ public String getAlgorithmName() {
+ return this.raw.getAlgorithm();
+ }
+
+ @Override
+ public int getBlockSize() {
+ return this.raw.getBlockSize();
+ }
+
+ @Override
+ public int getOutputSize(final int len) {
+ return this.raw.getOutputSize(len);
+ }
+
+ @Override
+ public void init(final CipherMode mode, final Parameters parameters) {
+ Assert.isInstanceOf(JceParameters.class, parameters, "Only support JceParameters!");
+
+ try {
+ init(mode.getValue(), (JceParameters) parameters);
+ } catch (final InvalidAlgorithmParameterException | InvalidKeyException e) {
+ throw new CryptoException(e);
+ }
+ }
+
+ /**
+ * 执行初始化参数操作
+ *
+ * @param mode 模式
+ * @param jceParameters {@link JceParameters}
+ * @throws InvalidAlgorithmParameterException 无效算法参数
+ * @throws InvalidKeyException 无效key
+ */
+ public void init(final int mode, final JceParameters jceParameters) throws InvalidAlgorithmParameterException, InvalidKeyException {
+ final javax.crypto.Cipher cipher = this.raw;
+ if (null != jceParameters.parameterSpec) {
+ if (null != jceParameters.random) {
+ cipher.init(mode, jceParameters.key, jceParameters.parameterSpec, jceParameters.random);
+ } else {
+ cipher.init(mode, jceParameters.key, jceParameters.parameterSpec);
+ }
+ } else {
+ if (null != jceParameters.random) {
+ cipher.init(mode, jceParameters.key, jceParameters.random);
+ } else {
+ cipher.init(mode, jceParameters.key);
+ }
+ }
+ }
+
+ @Override
+ public int process(final byte[] in, final int inOff, final int len, final byte[] out, final int outOff) {
+ try {
+ return this.raw.update(in, inOff, len, out, outOff);
+ } catch (final ShortBufferException e) {
+ throw new CryptoException(e);
+ }
+ }
+
+ @Override
+ public int doFinal(final byte[] out, final int outOff) {
+ try {
+ return this.raw.doFinal(out, outOff);
+ } catch (final Exception e) {
+ throw new CryptoException(e);
+ }
+ }
+
+ @Override
+ public byte[] processFinal(final byte[] data) {
+ try {
+ return this.raw.doFinal(data);
+ } catch (final Exception e) {
+ throw new CryptoException(e);
+ }
+ }
+
+ /**
+ * JCE的{@link AlgorithmParameterSpec} 参数包装
+ */
+ public static class JceParameters implements Parameters {
+ private final Key key;
+ /**
+ * 算法参数
+ */
+ private final AlgorithmParameterSpec parameterSpec;
+ /**
+ * 随机数生成器,可自定义随机数种子
+ */
+ private final SecureRandom random;
+
+ /**
+ * 构造
+ *
+ * @param key 密钥
+ * @param parameterSpec {@link AlgorithmParameterSpec}
+ * @param random 自定义随机数生成器
+ */
+ public JceParameters(final Key key, final AlgorithmParameterSpec parameterSpec, final SecureRandom random) {
+ this.key = key;
+ this.parameterSpec = parameterSpec;
+ this.random = random;
+ }
+ }
+}
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java
index be4eb5a98..43784426e 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/KeyUtil.java
@@ -64,6 +64,7 @@ public class KeyUtil {
public static final int DEFAULT_KEY_SIZE = 1024;
// region ----- generateKey
+
/**
* 生成 {@link SecretKey},仅用于对称加密和摘要算法密钥生成
*
@@ -196,7 +197,24 @@ public class KeyUtil {
}
// endregion
+ /**
+ * 检查{@link KeyPair} 是否为空,空的条件是:
+ *
+ * - keyPair本身为{@code null}
+ * - {@link KeyPair#getPrivate()}和{@link KeyPair#getPublic()}都为{@code null}
+ *
+ *
+ * @param keyPair 密钥对
+ * @return 是否为空
+ */
// region ----- keyPair
+ public static boolean isEmpty(final KeyPair keyPair) {
+ if (null == keyPair) {
+ return false;
+ }
+ return null != keyPair.getPrivate() || null != keyPair.getPublic();
+ }
+
/**
* 生成RSA私钥,仅用于非对称加密
* 采用PKCS#8规范,此规范定义了私钥信息语法和加密私钥语法
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Mode.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Mode.java
index 7ef557bae..87e592bfb 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Mode.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/Mode.java
@@ -54,5 +54,10 @@ public enum Mode {
/**
* Propagating Cipher Block
*/
- PCBC
+ PCBC,
+ /**
+ * GCM 全称为 Galois/Counter Mode。G是指GMAC,C是指CTR。
+ * 它在 CTR 加密的基础上增加 GMAC 的特性,解决了 CTR 不能对加密消息进行完整性校验的问题。
+ */
+ GCM
}
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SignUtil.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SignUtil.java
index b728bfc80..78ebbfc25 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SignUtil.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/SignUtil.java
@@ -31,7 +31,7 @@ import java.util.Map;
* 签名工具类
* 封装包括:
*
- * - 非堆成签名,签名算法支持见{@link SignAlgorithm}
+ * - 非对称签名,签名算法支持见{@link SignAlgorithm}
* - 对称签名,支持Map类型参数排序后签名
* - 摘要签名,支持Map类型参数排序后签名,签名方法见:{@link DigestAlgorithm}
*
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/asymmetric/AsymmetricCrypto.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/asymmetric/AsymmetricCrypto.java
index b050f6bf8..5542d8395 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/asymmetric/AsymmetricCrypto.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/asymmetric/AsymmetricCrypto.java
@@ -14,18 +14,13 @@ package org.dromara.hutool.crypto.asymmetric;
import org.dromara.hutool.core.codec.binary.Base64;
import org.dromara.hutool.core.io.stream.FastByteArrayOutputStream;
-import org.dromara.hutool.crypto.CipherWrapper;
-import org.dromara.hutool.crypto.CryptoException;
-import org.dromara.hutool.crypto.KeyUtil;
-import org.dromara.hutool.crypto.SecureUtil;
+import org.dromara.hutool.crypto.*;
import org.dromara.hutool.crypto.symmetric.SymmetricAlgorithm;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.IOException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -51,8 +46,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
/**
* Cipher负责完成加密或解密工作
*/
- protected CipherWrapper cipherWrapper;
-
+ protected JceCipher cipher;
/**
* 加密的块大小
*/
@@ -61,6 +55,15 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
* 解密的块大小
*/
protected int decryptBlockSize = -1;
+
+ /**
+ * 算法参数
+ */
+ private AlgorithmParameterSpec algorithmParameterSpec;
+ /**
+ * 自定义随机数
+ */
+ private SecureRandom random;
// ------------------------------------------------------------------ Constructor start
/**
@@ -144,8 +147,8 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
*/
public AsymmetricCrypto(final String algorithm, final byte[] privateKey, final byte[] publicKey) {
this(algorithm, //
- KeyUtil.generatePrivateKey(algorithm, privateKey), //
- KeyUtil.generatePublicKey(algorithm, publicKey)//
+ KeyUtil.generatePrivateKey(algorithm, privateKey), //
+ KeyUtil.generatePublicKey(algorithm, publicKey)//
);
}
@@ -209,7 +212,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
* @since 5.4.3
*/
public AlgorithmParameterSpec getAlgorithmParameterSpec() {
- return this.cipherWrapper.getParams();
+ return this.algorithmParameterSpec;
}
/**
@@ -217,10 +220,11 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
* 在某些算法中,需要特别的参数,例如在ECIES中,此处为IESParameterSpec
*
* @param algorithmParameterSpec {@link AlgorithmParameterSpec}
- * @since 5.4.3
+ * @return this
*/
- public void setAlgorithmParameterSpec(final AlgorithmParameterSpec algorithmParameterSpec) {
- this.cipherWrapper.setParams(algorithmParameterSpec);
+ public AsymmetricCrypto setAlgorithmParameterSpec(final AlgorithmParameterSpec algorithmParameterSpec) {
+ this.algorithmParameterSpec = algorithmParameterSpec;
+ return this;
}
/**
@@ -231,7 +235,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
* @since 5.7.17
*/
public AsymmetricCrypto setRandom(final SecureRandom random) {
- this.cipherWrapper.setRandom(random);
+ this.random = random;
return this;
}
@@ -249,7 +253,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
final Key key = getKeyByType(keyType);
lock.lock();
try {
- final Cipher cipher = initMode(Cipher.ENCRYPT_MODE, key);
+ final JceCipher cipher = initMode(CipherMode.ENCRYPT, key);
if (this.encryptBlockSize < 0) {
// 在引入BC库情况下,自动获取块大小
@@ -274,7 +278,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
final Key key = getKeyByType(keyType);
lock.lock();
try {
- final Cipher cipher = initMode(Cipher.DECRYPT_MODE, key);
+ final JceCipher cipher = initMode(CipherMode.DECRYPT, key);
if (this.decryptBlockSize < 0) {
// 在引入BC库情况下,自动获取块大小
@@ -301,7 +305,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
* @since 5.4.3
*/
public Cipher getCipher() {
- return this.cipherWrapper.getRaw();
+ return this.cipher.getRaw();
}
/**
@@ -310,7 +314,7 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
* @since 4.5.2
*/
protected void initCipher() {
- this.cipherWrapper = new CipherWrapper(this.algorithm);
+ this.cipher = new JceCipher(this.algorithm);
}
/**
@@ -346,9 +350,10 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
* @throws BadPaddingException padding错误异常
* @throws IOException IO异常,不会被触发
*/
+ @SuppressWarnings("resource")
private byte[] doFinalWithBlock(final byte[] data, final int maxBlockSize) throws IllegalBlockSizeException, BadPaddingException, IOException {
final int dataLength = data.length;
- @SuppressWarnings("resource") final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
+ final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
int offSet = 0;
// 剩余长度
@@ -367,15 +372,15 @@ public class AsymmetricCrypto extends AbstractAsymmetricCrypto
}
/**
- * 初始化{@link Cipher}的模式,如加密模式或解密模式
+ * 初始化{@link JceCipher}的模式,如加密模式或解密模式
*
- * @param mode 模式,可选{@link Cipher#ENCRYPT_MODE}或者{@link Cipher#DECRYPT_MODE}
+ * @param mode 模式,可选{@link CipherMode#ENCRYPT}或者{@link CipherMode#DECRYPT}
* @param key 密钥
- * @return {@link Cipher}
- * @throws InvalidAlgorithmParameterException 异常算法错误
- * @throws InvalidKeyException 异常KEY错误
+ * @return {@link JceCipher}
*/
- private Cipher initMode(final int mode, final Key key) throws InvalidAlgorithmParameterException, InvalidKeyException {
- return this.cipherWrapper.initMode(mode, key).getRaw();
+ private JceCipher initMode(final CipherMode mode, final Key key) {
+ final JceCipher cipher = this.cipher;
+ cipher.init(mode, new JceCipher.JceParameters(key, this.algorithmParameterSpec, this.random));
+ return cipher;
}
}
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/bc/BCCipher.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/bc/BCCipher.java
new file mode 100644
index 000000000..3281675a1
--- /dev/null
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/bc/BCCipher.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2024. looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * https://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.crypto.bc;
+
+import org.bouncycastle.crypto.*;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.dromara.hutool.core.lang.Assert;
+import org.dromara.hutool.core.lang.wrapper.Wrapper;
+import org.dromara.hutool.crypto.Cipher;
+import org.dromara.hutool.crypto.CipherMode;
+import org.dromara.hutool.crypto.CryptoException;
+
+import java.util.Arrays;
+
+/**
+ * 基于BouncyCastle库封装的加密解密实现,包装包括:
+ *
+ * - {@link BufferedBlockCipher}
+ * - {@link BlockCipher}
+ * - {@link StreamCipher}
+ * - {@link AEADBlockCipher}
+ *
+ *
+ * @author Looly, changhr2013
+ */
+public class BCCipher implements Cipher, Wrapper
*
+ * {@code
+ * MacEngineFactory
+ * ||(创建)
+ * MacEngine----------------(包装)-----------------> Mac
+ * _____|_______________ |
+ * / \ HMac
+ * JCEMacEngine BCMacEngine
+ * / \
+ * BCHMacEngine CBCBlockCipherMacEngine
+ * |
+ * SM4MacEngine
+ * }
+ *
+ * 通过MacEngine,封装支持了BouncyCastle和JCE实现的一些MAC算法,通过MacEngineFactory自动根据算法名称创建对应对象。
+ *
* @author Looly
* @since 4.5.13
*/
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/otp/package-info.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/otp/package-info.java
index 29e540aac..695306155 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/otp/package-info.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/digest/otp/package-info.java
@@ -21,6 +21,12 @@
* Truncate:是一个函数,就是怎么截取加密后的串,并取加密后串的哪些字段组成一个数字。
*
*
+ * 实现包括:
+ *
+ * - HMAC-based one-time passwords (HOTP) 基于HMAC算法一次性密码生成器
+ * - time-based one-time passwords (TOTP) 基于时间戳算法的一次性密码生成器
+ *
+ *
* @author looly
*/
package org.dromara.hutool.crypto.digest.otp;
diff --git a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/symmetric/SymmetricCrypto.java b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/symmetric/SymmetricCrypto.java
index 38cea58af..a96eb09d6 100644
--- a/hutool-crypto/src/main/java/org/dromara/hutool/crypto/symmetric/SymmetricCrypto.java
+++ b/hutool-crypto/src/main/java/org/dromara/hutool/crypto/symmetric/SymmetricCrypto.java
@@ -34,8 +34,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.concurrent.locks.Lock;
@@ -52,7 +50,15 @@ import java.util.concurrent.locks.ReentrantLock;
public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor, Serializable {
private static final long serialVersionUID = 1L;
- private CipherWrapper cipherWrapper;
+ private JceCipher cipher;
+ /**
+ * 算法参数
+ */
+ private AlgorithmParameterSpec algorithmParameterSpec;
+ /**
+ * 自定义随机数
+ */
+ private SecureRandom random;
/**
* SecretKey 负责保存对称密钥
*/
@@ -157,7 +163,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
this.isZeroPadding = true;
}
- this.cipherWrapper = new CipherWrapper(algorithm);
+ this.cipher = new JceCipher(algorithm);
return this;
}
@@ -176,17 +182,17 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
* @return 加密或解密
*/
public Cipher getCipher() {
- return cipherWrapper.getRaw();
+ return cipher.getRaw();
}
/**
- * 设置 {@link AlgorithmParameterSpec},通常用于加盐或偏移向量
+ * 设置{@link AlgorithmParameterSpec},通常用于加盐或偏移向量
*
- * @param params {@link AlgorithmParameterSpec}
- * @return 自身
+ * @param algorithmParameterSpec {@link AlgorithmParameterSpec}
+ * @return this
*/
- public SymmetricCrypto setParams(final AlgorithmParameterSpec params) {
- this.cipherWrapper.setParams(params);
+ public SymmetricCrypto setAlgorithmParameterSpec(final AlgorithmParameterSpec algorithmParameterSpec) {
+ this.algorithmParameterSpec = algorithmParameterSpec;
return this;
}
@@ -197,7 +203,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
* @return 自身
*/
public SymmetricCrypto setIv(final IvParameterSpec iv) {
- return setParams(iv);
+ return setAlgorithmParameterSpec(iv);
}
/**
@@ -218,7 +224,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
* @since 5.7.17
*/
public SymmetricCrypto setRandom(final SecureRandom random) {
- this.cipherWrapper.setRandom(random);
+ this.random = random;
return this;
}
@@ -245,9 +251,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
public SymmetricCrypto setMode(final CipherMode mode, final byte[] salt) {
lock.lock();
try {
- initMode(mode.getValue(), salt);
- } catch (final Exception e) {
- throw new CryptoException(e);
+ initMode(mode, salt);
} finally {
lock.unlock();
}
@@ -263,7 +267,7 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
* @since 5.6.8
*/
public byte[] update(final byte[] data) {
- final Cipher cipher = cipherWrapper.getRaw();
+ final Cipher cipher = this.cipher.getRaw();
lock.lock();
try {
return cipher.update(paddingDataWithZero(data, cipher.getBlockSize()));
@@ -305,10 +309,8 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
byte[] result;
lock.lock();
try {
- final Cipher cipher = initMode(Cipher.ENCRYPT_MODE, salt);
- result = cipher.doFinal(paddingDataWithZero(data, cipher.getBlockSize()));
- } catch (final Exception e) {
- throw new CryptoException(e);
+ final JceCipher cipher = initMode(CipherMode.ENCRYPT, salt);
+ result = cipher.processFinal(paddingDataWithZero(data, cipher.getBlockSize()));
} finally {
lock.unlock();
}
@@ -320,8 +322,8 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
CipherOutputStream cipherOutputStream = null;
lock.lock();
try {
- final Cipher cipher = initMode(Cipher.ENCRYPT_MODE, null);
- cipherOutputStream = new CipherOutputStream(out, cipher);
+ final JceCipher cipher = initMode(CipherMode.ENCRYPT, null);
+ cipherOutputStream = new CipherOutputStream(out, cipher.getRaw());
final long length = IoUtil.copy(data, cipherOutputStream);
if (this.isZeroPadding) {
final int blockSize = cipher.getBlockSize();
@@ -359,9 +361,9 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
lock.lock();
try {
final byte[] salt = SaltMagic.getSalt(bytes);
- final Cipher cipher = initMode(Cipher.DECRYPT_MODE, salt);
+ final JceCipher cipher = initMode(CipherMode.DECRYPT, salt);
blockSize = cipher.getBlockSize();
- decryptData = cipher.doFinal(SaltMagic.getData(bytes));
+ decryptData = cipher.processFinal(SaltMagic.getData(bytes));
} catch (final Exception e) {
throw new CryptoException(e);
} finally {
@@ -376,8 +378,8 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
CipherInputStream cipherInputStream = null;
lock.lock();
try {
- final Cipher cipher = initMode(Cipher.DECRYPT_MODE, null);
- cipherInputStream = new CipherInputStream(data, cipher);
+ final JceCipher cipher = initMode(CipherMode.DECRYPT, null);
+ cipherInputStream = new CipherInputStream(data, cipher.getRaw());
if (this.isZeroPadding) {
final int blockSize = cipher.getBlockSize();
if (blockSize > 0) {
@@ -417,8 +419,8 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
*/
private SymmetricCrypto initParams(final String algorithm, AlgorithmParameterSpec paramsSpec) {
if (null == paramsSpec) {
- byte[] iv = Opt.ofNullable(cipherWrapper)
- .map(CipherWrapper::getRaw).map(Cipher::getIV).get();
+ byte[] iv = Opt.ofNullable(cipher)
+ .map(JceCipher::getRaw).map(Cipher::getIV).get();
// 随机IV
if (StrUtil.startWithIgnoreCase(algorithm, "PBE")) {
@@ -435,18 +437,16 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
}
}
- return setParams(paramsSpec);
+ return setAlgorithmParameterSpec(paramsSpec);
}
/**
- * 初始化{@link Cipher}为加密或者解密模式
+ * 初始化{@link JceCipher}为加密或者解密模式
*
- * @param mode 模式,见{@link Cipher#ENCRYPT_MODE} 或 {@link Cipher#DECRYPT_MODE}
+ * @param mode 模式,见{@link CipherMode#ENCRYPT} 或 {@link CipherMode#DECRYPT}
* @return {@link Cipher}
- * @throws InvalidKeyException 无效key
- * @throws InvalidAlgorithmParameterException 无效算法
*/
- private Cipher initMode(final int mode, final byte[] salt) throws InvalidKeyException, InvalidAlgorithmParameterException {
+ private JceCipher initMode(final CipherMode mode, final byte[] salt) {
SecretKey secretKey = this.secretKey;
if (null != salt) {
// /issues#I6YWWD,提供OpenSSL格式兼容支持
@@ -454,11 +454,15 @@ public class SymmetricCrypto implements SymmetricEncryptor, SymmetricDecryptor,
final byte[][] keyAndIV = OpenSSLSaltParser.ofMd5(32, algorithm)
.getKeyAndIV(secretKey.getEncoded(), salt);
secretKey = KeyUtil.generateKey(algorithm, keyAndIV[0]);
- if(ArrayUtil.isNotEmpty(keyAndIV[1])){
- this.cipherWrapper.setParams(new IvParameterSpec(keyAndIV[1]));
+ if (ArrayUtil.isNotEmpty(keyAndIV[1])) {
+ setAlgorithmParameterSpec(new IvParameterSpec(keyAndIV[1]));
}
}
- return this.cipherWrapper.initMode(mode, secretKey).getRaw();
+
+ final JceCipher cipher = this.cipher;
+ cipher.init(mode,
+ new JceCipher.JceParameters(secretKey, this.algorithmParameterSpec, this.random));
+ return cipher;
}
/**
diff --git a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/bc/BCCipherTest.java b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/bc/BCCipherTest.java
new file mode 100644
index 000000000..b614c1ad5
--- /dev/null
+++ b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/bc/BCCipherTest.java
@@ -0,0 +1,28 @@
+package org.dromara.hutool.crypto.bc;
+
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.DefaultBufferedBlockCipher;
+import org.bouncycastle.crypto.engines.SM4Engine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.dromara.hutool.core.util.ByteUtil;
+import org.dromara.hutool.crypto.CipherMode;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class BCCipherTest {
+ @Test
+ void sm4Test() {
+ final byte[] data = ByteUtil.toUtf8Bytes("我是测试Hutool的字符串00");
+
+ final BufferedBlockCipher blockCipher = new DefaultBufferedBlockCipher(CBCBlockCipher.newInstance(new SM4Engine()));
+ final BCCipher bcCipher = new BCCipher(blockCipher);
+ bcCipher.init(CipherMode.ENCRYPT, new BCCipher.BCParameters(new KeyParameter(ByteUtil.toUtf8Bytes("1234567890000000"))));
+ final byte[] encryptData = bcCipher.processFinal(data);
+
+ bcCipher.init(CipherMode.DECRYPT, new BCCipher.BCParameters(new KeyParameter(ByteUtil.toUtf8Bytes("1234567890000000"))));
+ final byte[] decryptData = bcCipher.processFinal(encryptData);
+
+ Assertions.assertArrayEquals(data, decryptData);
+ }
+}
diff --git a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/symmetric/SymmetricTest.java b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/symmetric/SymmetricTest.java
index bfb6d7d2f..7a939c9ee 100644
--- a/hutool-crypto/src/test/java/org/dromara/hutool/crypto/symmetric/SymmetricTest.java
+++ b/hutool-crypto/src/test/java/org/dromara/hutool/crypto/symmetric/SymmetricTest.java
@@ -135,9 +135,9 @@ public class SymmetricTest {
final AES aes = new AES(Mode.CBC, Padding.PKCS5Padding, "0123456789ABHAEQ".getBytes(), "DYgjCEIMVrj2W9xN".getBytes());
// 加密为16进制表示
- aes.setMode(CipherMode.encrypt);
+ aes.setMode(CipherMode.ENCRYPT);
final String randomData = aes.updateHex(content.getBytes(StandardCharsets.UTF_8));
- aes.setMode(CipherMode.encrypt);
+ aes.setMode(CipherMode.ENCRYPT);
final String randomData2 = aes.updateHex(content.getBytes(StandardCharsets.UTF_8));
Assertions.assertEquals(randomData2, randomData);
Assertions.assertEquals(randomData, "cd0e3a249eaf0ed80c330338508898c4");
diff --git a/hutool-db/README.md b/hutool-db/README.md
index 603876838..97d7fd3bb 100644
--- a/hutool-db/README.md
+++ b/hutool-db/README.md
@@ -28,7 +28,7 @@
### SQL相关工具(sql)
提供SQL相关功能,包括SQL变量替换(NamedSql),通过对象完成SQL构建(SqlBuilder)等。
-`SqlSqlExecutor`提供SQL执行的静态方法。
+`SqlExecutor`提供SQL执行的静态方法。
### 数据库元信息(meta)
通过`MetaUtil`提供数据库表、字段等信息的读取操作。
diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml
index bab350310..36cfd6d7a 100755
--- a/hutool-db/pom.xml
+++ b/hutool-db/pom.xml
@@ -21,7 +21,7 @@
org.dromara.hutool
hutool-parent
- 6.0.0-M10
+ 6.0.0-M11
hutool-db
@@ -38,7 +38,7 @@
1.2.21
4.0.3
- 3.43.0.0
+ 3.44.1.0
2.5.2
diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/StatementUtil.java b/hutool-db/src/main/java/org/dromara/hutool/db/StatementUtil.java
index 449109869..679faed72 100644
--- a/hutool-db/src/main/java/org/dromara/hutool/db/StatementUtil.java
+++ b/hutool-db/src/main/java/org/dromara/hutool/db/StatementUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 looly(loolly@aliyun.com)
+ * Copyright (c) 2023-2024. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
@@ -12,14 +12,15 @@
package org.dromara.hutool.db;
+import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.collection.iter.ArrayIter;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.db.handler.ResultSetUtil;
import org.dromara.hutool.db.handler.RsHandler;
import org.dromara.hutool.db.sql.SqlBuilder;
-import org.dromara.hutool.db.sql.SqlLog;
import org.dromara.hutool.db.sql.StatementBuilder;
import org.dromara.hutool.db.sql.StatementWrapper;
+import org.dromara.hutool.db.sql.filter.SqlLogFilter;
import java.sql.*;
import java.util.Collection;
@@ -87,7 +88,7 @@ public class StatementUtil {
return StatementBuilder.of()
.setConnection(conn)
.setReturnGeneratedKey(returnGeneratedKey)
- .setSqlLog(SqlLog.INSTANCE)
+ .setSqlFilter(SqlLogFilter.INSTANCE)
.setSql(sql)
.setParams(params)
.build();
@@ -120,29 +121,10 @@ public class StatementUtil {
return StatementBuilder.of()
.setConnection(conn)
.setReturnGeneratedKey(false)
- .setSqlLog(SqlLog.INSTANCE)
+ .setSqlFilter(SqlLogFilter.INSTANCE)
.setSql(sql)
- .buildForBatch(paramsBatch);
- }
-
- /**
- * 创建批量操作的{@link PreparedStatement}
- *
- * @param conn 数据库连接
- * @param sql SQL语句,使用"?"做为占位符
- * @param fields 字段列表,用于获取对应值
- * @param entities "?"对应参数批次列表
- * @return {@link PreparedStatement}
- * @since 4.6.7
- */
- public static PreparedStatement prepareStatementForBatch(final Connection conn, final String sql,
- final Iterable fields, final Entity... entities) {
- return StatementBuilder.of()
- .setConnection(conn)
- .setReturnGeneratedKey(false)
- .setSqlLog(SqlLog.INSTANCE)
- .setSql(sql)
- .buildForBatch(fields, entities);
+ .setParams(ArrayUtil.ofArray(paramsBatch, Object.class))
+ .buildForBatch();
}
/**
@@ -158,7 +140,7 @@ public class StatementUtil {
public static CallableStatement prepareCall(final Connection conn, final String sql, final Object... params) throws SQLException {
return StatementBuilder.of()
.setConnection(conn)
- .setSqlLog(SqlLog.INSTANCE)
+ .setSqlFilter(SqlLogFilter.INSTANCE)
.setSql(sql)
.setParams(params)
.buildForCall();
diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/AnsiSqlDialect.java b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/AnsiSqlDialect.java
index fdc6ff605..64bb94fe2 100644
--- a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/AnsiSqlDialect.java
+++ b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/AnsiSqlDialect.java
@@ -53,21 +53,20 @@ public class AnsiSqlDialect implements Dialect {
}
@Override
- public PreparedStatement psForInsert(final Connection conn, final Entity entity) throws SQLException {
+ public PreparedStatement psForInsert(final Connection conn, final Entity entity) {
final SqlBuilder insert = SqlBuilder.of(quoteWrapper).insert(entity, this.dialectName());
return StatementUtil.prepareStatement(conn, insert);
}
@Override
- public PreparedStatement psForInsertBatch(final Connection conn, final Entity... entities) throws SQLException {
+ public PreparedStatement psForInsertBatch(final Connection conn, final Entity... entities) {
if (ArrayUtil.isEmpty(entities)) {
throw new DbRuntimeException("Entities for batch insert is empty !");
}
// 批量,根据第一行数据结构生成SQL占位符
final SqlBuilder insert = SqlBuilder.of(quoteWrapper).insert(entities[0], this.dialectName());
- final Set fields = CollUtil.remove(entities[0].keySet(), StrUtil::isBlank);
- return StatementUtil.prepareStatementForBatch(conn, insert.build(), fields, entities);
+ return StatementUtil.prepareStatementForBatch(conn, insert.build(), entities);
}
@Override
@@ -100,12 +99,12 @@ public class AnsiSqlDialect implements Dialect {
}
@Override
- public PreparedStatement psForFind(final Connection conn, final Query query) throws SQLException {
+ public PreparedStatement psForFind(final Connection conn, final Query query) {
return psForPage(conn, query);
}
@Override
- public PreparedStatement psForPage(final Connection conn, final Query query) throws SQLException {
+ public PreparedStatement psForPage(final Connection conn, final Query query) {
Assert.notNull(query, "query must be not null !");
if (ArrayUtil.hasBlank(query.getTableNames())) {
throw new DbRuntimeException("Table name must be not empty !");
@@ -116,7 +115,7 @@ public class AnsiSqlDialect implements Dialect {
}
@Override
- public PreparedStatement psForPage(final Connection conn, SqlBuilder sqlBuilder, final Page page) throws SQLException {
+ public PreparedStatement psForPage(final Connection conn, SqlBuilder sqlBuilder, final Page page) {
// 根据不同数据库在查询SQL语句基础上包装其分页的语句
if (null != page) {
sqlBuilder = wrapPageSql(sqlBuilder.orderBy(page.getOrders()), page);
diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/OracleDialect.java b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/OracleDialect.java
index 06f822a46..de67b8612 100644
--- a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/OracleDialect.java
+++ b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/OracleDialect.java
@@ -12,6 +12,7 @@
package org.dromara.hutool.db.dialect.impl;
+import org.dromara.hutool.core.text.StrPool;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.Page;
import org.dromara.hutool.db.dialect.DialectName;
@@ -25,6 +26,10 @@ import org.dromara.hutool.db.sql.SqlBuilder;
public class OracleDialect extends AnsiSqlDialect {
private static final long serialVersionUID = 6122761762247483015L;
+ private static final String DEFAULT_TABLE_ALIAS = "table_alias_";
+ private static final String DEFAULT_ROW_ALIAS = "row_";
+ private static final String DEFAULT_ROWNUM_ALIAS = "rownum_";
+
/**
* 检查字段值是否为Oracle自增字段,自增字段以`.nextval`结尾
*
@@ -36,6 +41,9 @@ public class OracleDialect extends AnsiSqlDialect {
return (value instanceof CharSequence) && StrUtil.endWithIgnoreCase(value.toString(), ".nextval");
}
+ /**
+ * 构造
+ */
public OracleDialect() {
//Oracle所有字段名用双引号包围,防止字段名或表名与系统关键字冲突
//wrapper = new Wrapper('"');
@@ -44,11 +52,27 @@ public class OracleDialect extends AnsiSqlDialect {
@Override
protected SqlBuilder wrapPageSql(final SqlBuilder find, final Page page) {
final int[] startEnd = page.getStartEnd();
+
+ // 检查别名,避免重名
+ final String sql = find.toString();
+ String tableAlias = DEFAULT_TABLE_ALIAS;
+ while (sql.contains(tableAlias)) {
+ tableAlias += StrPool.UNDERLINE;
+ }
+ String rowAlias = DEFAULT_ROW_ALIAS;
+ while (sql.contains(rowAlias)) {
+ rowAlias += StrPool.UNDERLINE;
+ }
+ String rownumAlias = DEFAULT_ROWNUM_ALIAS;
+ while (sql.contains(rownumAlias)) {
+ rownumAlias += StrPool.UNDERLINE;
+ }
+
return find
- .insertPreFragment("SELECT * FROM ( SELECT row_.*, rownum rownum_ from ( ")
- .append(" ) row_ where rownum <= ").append(startEnd[1])//
- .append(") table_alias_")//
- .append(" where table_alias_.rownum_ > ").append(startEnd[0]);//
+ .insertPreFragment("SELECT * FROM ( SELECT " + rowAlias + ".*, rownum " + rownumAlias + " from ( ")
+ .append(" ) row_ where rownum <= ").append(startEnd[1])//
+ .append(") ").append(tableAlias)//
+ .append(" where ").append(tableAlias).append(".rownum_ > ").append(startEnd[0]);//
}
@Override
diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/PhoenixDialect.java b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/PhoenixDialect.java
index ebfa95146..0d9966ab0 100644
--- a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/PhoenixDialect.java
+++ b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/PhoenixDialect.java
@@ -29,6 +29,9 @@ import java.sql.SQLException;
public class PhoenixDialect extends AnsiSqlDialect {
private static final long serialVersionUID = 1L;
+ /**
+ * 构造
+ */
public PhoenixDialect() {
// wrapper = new Wrapper('"');
}
diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/SqlServer2012Dialect.java b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/SqlServer2012Dialect.java
index fd98c9ebf..b879b0195 100644
--- a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/SqlServer2012Dialect.java
+++ b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/SqlServer2012Dialect.java
@@ -21,15 +21,17 @@ import org.dromara.hutool.db.sql.QuoteWrapper;
/**
* SQLServer2012 方言
*
- * @author loolly
- *
+ * @author Looly
*/
public class SqlServer2012Dialect extends AnsiSqlDialect {
private static final long serialVersionUID = -37598166015777797L;
+ /**
+ * 构造
+ */
public SqlServer2012Dialect() {
//双引号和中括号适用,双引号更广泛
- quoteWrapper = new QuoteWrapper('"');
+ quoteWrapper = new QuoteWrapper('"');
}
@Override
@@ -39,10 +41,10 @@ public class SqlServer2012Dialect extends AnsiSqlDialect {
find.append(" order by current_timestamp");
}
return find.append(" offset ")
- .append(page.getStartPosition())//
- .append(" row fetch next ")//row和rows同义词
- .append(page.getPageSize())//
- .append(" row only");//
+ .append(page.getStartPosition())//
+ .append(" row fetch next ")//row和rows同义词
+ .append(page.getPageSize())//
+ .append(" row only");//
}
@Override
diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/Sqlite3Dialect.java b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/Sqlite3Dialect.java
index d8209e501..fc9217b2f 100644
--- a/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/Sqlite3Dialect.java
+++ b/hutool-db/src/main/java/org/dromara/hutool/db/dialect/impl/Sqlite3Dialect.java
@@ -17,12 +17,15 @@ import org.dromara.hutool.db.sql.QuoteWrapper;
/**
* SqlLite3方言
- * @author loolly
*
+ * @author Looly
*/
-public class Sqlite3Dialect extends AnsiSqlDialect{
+public class Sqlite3Dialect extends AnsiSqlDialect {
private static final long serialVersionUID = -3527642408849291634L;
+ /**
+ * 构造
+ */
public Sqlite3Dialect() {
quoteWrapper = new QuoteWrapper('[', ']');
}
diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSPool.java b/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSPool.java
index 20cc1aa0b..90cef3077 100644
--- a/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSPool.java
+++ b/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSPool.java
@@ -19,6 +19,7 @@ import org.dromara.hutool.core.map.SafeConcurrentHashMap;
import org.dromara.hutool.core.spi.SpiUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.db.DbRuntimeException;
+import org.dromara.hutool.db.DbUtil;
import org.dromara.hutool.db.GlobalDbConfig;
import org.dromara.hutool.db.driver.DriverUtil;
import org.dromara.hutool.log.LogUtil;
@@ -89,6 +90,7 @@ public class DSPool implements Closeable {
*/
public DSPool(final Setting setting, final DSFactory factory) {
this.setting = null != setting ? setting : GlobalDbConfig.createDbSetting();
+ DbUtil.setShowSqlGlobal(this.setting);
this.factory = null != factory ? factory : SpiUtil.loadFirstAvailable(DSFactory.class);
this.pool = new SafeConcurrentHashMap<>();
}
diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSWrapper.java b/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSWrapper.java
index 1109de772..2490387ca 100644
--- a/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSWrapper.java
+++ b/hutool-db/src/main/java/org/dromara/hutool/db/ds/DSWrapper.java
@@ -14,7 +14,7 @@ package org.dromara.hutool.db.ds;
import org.dromara.hutool.core.exception.CloneException;
import org.dromara.hutool.core.io.IoUtil;
-import org.dromara.hutool.core.lang.wrapper.Wrapper;
+import org.dromara.hutool.core.lang.wrapper.SimpleWrapper;
import javax.sql.DataSource;
import java.io.Closeable;
@@ -34,9 +34,8 @@ import java.util.logging.Logger;
* @author looly
* @since 4.3.2
*/
-public class DSWrapper implements Wrapper, DataSource, Closeable, Cloneable {
+public class DSWrapper extends SimpleWrapper implements DataSource, Closeable, Cloneable {
- private final DataSource ds;
private final String driver;
/**
@@ -57,7 +56,7 @@ public class DSWrapper implements Wrapper, DataSource, Closeable, Cl
* @param driver 数据库驱动类名
*/
public DSWrapper(final DataSource ds, final String driver) {
- this.ds = ds;
+ super(ds);
this.driver = driver;
}
@@ -70,65 +69,56 @@ public class DSWrapper implements Wrapper, DataSource, Closeable, Cl
return this.driver;
}
- /**
- * 获取原始的数据源
- *
- * @return 原始数据源
- */
- @Override
- public DataSource getRaw() {
- return this.ds;
- }
-
@Override
public PrintWriter getLogWriter() throws SQLException {
- return ds.getLogWriter();
+ return this.raw.getLogWriter();
}
@Override
public void setLogWriter(final PrintWriter out) throws SQLException {
- ds.setLogWriter(out);
+ this.raw.setLogWriter(out);
}
@Override
public void setLoginTimeout(final int seconds) throws SQLException {
- ds.setLoginTimeout(seconds);
+ this.raw.setLoginTimeout(seconds);
}
@Override
public int getLoginTimeout() throws SQLException {
- return ds.getLoginTimeout();
+ return this.raw.getLoginTimeout();
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
- return ds.getParentLogger();
+ return this.raw.getParentLogger();
}
@Override
public T unwrap(final Class iface) throws SQLException {
- return ds.unwrap(iface);
+ return this.raw.unwrap(iface);
}
@Override
public boolean isWrapperFor(final Class> iface) throws SQLException {
- return ds.isWrapperFor(iface);
+ return this.raw.isWrapperFor(iface);
}
@Override
public Connection getConnection() throws SQLException {
- return ds.getConnection();
+ return this.raw.getConnection();
}
@Override
public Connection getConnection(final String username, final String password) throws SQLException {
- return ds.getConnection(username, password);
+ return this.raw.getConnection(username, password);
}
@Override
public void close() {
- if (this.ds instanceof AutoCloseable) {
- IoUtil.closeQuietly((AutoCloseable) this.ds);
+ final DataSource ds = this.raw;
+ if (ds instanceof AutoCloseable) {
+ IoUtil.closeQuietly((AutoCloseable) ds);
}
}
diff --git a/hutool-db/src/main/java/org/dromara/hutool/db/sql/BoundSql.java b/hutool-db/src/main/java/org/dromara/hutool/db/sql/BoundSql.java
new file mode 100644
index 000000000..1801c08c7
--- /dev/null
+++ b/hutool-db/src/main/java/org/dromara/hutool/db/sql/BoundSql.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2024. looly(loolly@aliyun.com)
+ * Hutool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * https://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ */
+
+package org.dromara.hutool.db.sql;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 参数绑定的SQL封装,用于表示SQL语句模板('?'表示参数占位符)和参数值的封装
+ * SQL中的'?'占位符必须和params列表中的参数值一一对应
+ *
+ * @author Looly
+ */
+public class BoundSql {
+
+ private String sql;
+ private List