diff --git a/CHANGELOG.md b/CHANGELOG.md index dfefa113b..e7434e9d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # 🚀Changelog ------------------------------------------------------------------------------------------------------------- -# 5.7.23 (2022-03-09) +# 5.7.23 (2022-03-15) ### 🐣新特性 * 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee) @@ -11,11 +11,18 @@ * 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee) * 【core 】 增加Table实现(issue#2179@Github) * 【core 】 增加UniqueKeySet(issue#I4WUWR@Gitee) +* 【core 】 阿拉伯数字转换成中文对发票票面金额转换的扩展(pr#570@Gitee) +* 【core 】 ArrayUtil增加replace方法(pr#570@Gitee) +* 【core 】 CsvReadConfig增加自定义标题行行号(issue#2180@Github) +* 【db 】 增加MongoDB4.x支持(pr#568@Gitee) * ### 🐞Bug修复 * 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee) * 【core 】 修复NumberConverter对数字转换的问题(issue#I4WPF4@Gitee) * 【core 】 修复ReflectUtil.getMethods获取接口方法问题(issue#I4WUWR@Gitee) +* 【core 】 修复NamingCase中大写转换问题(pr#572@Gitee) +* 【http 】 修复GET重定向时,携带参数问题(issue#2189@Github) +* 【core 】 修复FileUtil、FileCopier相对路径获取父路径错误问题(pr#2188@Github) ------------------------------------------------------------------------------------------------------------- # 5.7.22 (2022-03-01) diff --git a/README-EN.md b/README-EN.md index 05475e644..7456a08f7 100644 --- a/README-EN.md +++ b/README-EN.md @@ -111,6 +111,8 @@ Each module can be introduced individually, or all modules can be introduced by [📘Chinese documentation](https://www.hutool.cn/docs/) +[📘Chinese back-up documentation](https://plus.hutool.cn/docs/#/) + [📙API](https://apidoc.gitee.com/dromara/hutool/) [🎬Video](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2) diff --git a/README.md b/README.md index f8834f52a..409e4e22d 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,8 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不 [📘中文文档](https://www.hutool.cn/docs/) +[📘中文备用文档](https://plus.hutool.cn/docs/#/) + [📙参考API](https://apidoc.gitee.com/dromara/hutool/) [🎬视频介绍](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2) diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java index 2a315d38d..6ec3f0e16 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java @@ -40,7 +40,6 @@ public class NumberChineseFormatter { new ChineseUnit('亿', 1_0000_0000, true), }; - /** * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换. * @@ -53,15 +52,26 @@ public class NumberChineseFormatter { } /** - * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换. + * 阿拉伯数字转换成中文. + * + *

主要是对发票票面金额转换的扩展 + *

如:-12.32 + *

发票票面转换为:(负数)壹拾贰圆叁角贰分 + *

而非:负壹拾贰元叁角贰分 + *

共两点不同:1、(负数) 而非 负;2、圆 而非 元 + * 2022/3/9 * * @param amount 数字 * @param isUseTraditional 是否使用繁体 - * @param isMoneyMode 是否为金额模式 - * @return 中文 + * @param isMoneyMode 是否金额模式 + * @param negativeName 负号转换名称 如:负、(负数) + * @param unitName 单位名称 如:元、圆 + * @return java.lang.String + * @author machuanpeng + * @since 5.7.23 */ - public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode) { - if(0 == amount){ + public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode, String negativeName, String unitName) { + if (0 == amount) { return "零"; } Assert.checkBetween(amount, -99_9999_9999_9999.99, 99_9999_9999_9999.99, @@ -71,7 +81,7 @@ public class NumberChineseFormatter { // 负数 if (amount < 0) { - chineseStr.append("负"); + chineseStr.append(StrUtil.isNullOrUndefined(negativeName) ? "负" : negativeName); amount = -amount; } @@ -82,44 +92,44 @@ public class NumberChineseFormatter { yuan = yuan / 10; // 元 - if(false == isMoneyMode || 0 != yuan){ + if (false == isMoneyMode || 0 != yuan) { // 金额模式下,无需“零元” chineseStr.append(longToChinese(yuan, isUseTraditional)); - if(isMoneyMode){ - chineseStr.append("元"); + if (isMoneyMode) { + chineseStr.append(StrUtil.isNullOrUndefined(unitName) ? "元" : unitName); } } - if(0 == jiao && 0 == fen){ + if (0 == jiao && 0 == fen) { //无小数部分的金额结尾 - if(isMoneyMode){ + if (isMoneyMode) { chineseStr.append("整"); } return chineseStr.toString(); } // 小数部分 - if(false == isMoneyMode){ + if (false == isMoneyMode) { chineseStr.append("点"); } // 角 - if(0 == yuan && 0 == jiao){ + if (0 == yuan && 0 == jiao) { // 元和角都为0时,只有非金额模式下补“零” - if(false == isMoneyMode){ + if (false == isMoneyMode) { chineseStr.append("零"); } - }else{ + } else { chineseStr.append(numberToChinese(jiao, isUseTraditional)); - if(isMoneyMode && 0 != jiao){ + if (isMoneyMode && 0 != jiao) { chineseStr.append("角"); } } // 分 - if(0 != fen){ + if (0 != fen) { chineseStr.append(numberToChinese(fen, isUseTraditional)); - if(isMoneyMode){ + if (isMoneyMode) { chineseStr.append("分"); } } @@ -127,16 +137,28 @@ public class NumberChineseFormatter { return chineseStr.toString(); } + /** + * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换. + * + * @param amount 数字 + * @param isUseTraditional 是否使用繁体 + * @param isMoneyMode 是否为金额模式 + * @return 中文 + */ + public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode) { + return format(amount, isUseTraditional, isMoneyMode, "负", "元"); + } + /** * 阿拉伯数字(支持正负整数)转换成中文 * - * @param amount 数字 + * @param amount 数字 * @param isUseTraditional 是否使用繁体 * @return 中文 * @since 5.7.17 */ - public static String format(long amount, boolean isUseTraditional){ - if(0 == amount){ + public static String format(long amount, boolean isUseTraditional) { + if (0 == amount) { return "零"; } Assert.checkBetween(amount, -99_9999_9999_9999.99, 99_9999_9999_9999.99, @@ -179,16 +201,16 @@ public class NumberChineseFormatter { * 格式化-999~999之间的数字
* 这个方法显示10~19以下的数字时使用"十一"而非"一十一"。 * - * @param amount 数字 + * @param amount 数字 * @param isUseTraditional 是否使用繁体 * @return 中文 * @since 5.7.17 */ - public static String formatThousand(int amount, boolean isUseTraditional){ + public static String formatThousand(int amount, boolean isUseTraditional) { Assert.checkBetween(amount, -999, 999, "Number support only: (-999 ~ 999)!"); final String chinese = thousandToChinese(amount, isUseTraditional); - if(amount < 20 && amount >= 10){ + if (amount < 20 && amount >= 10) { // "十一"而非"一十一" return chinese.substring(1); } @@ -218,7 +240,7 @@ public class NumberChineseFormatter { * @return 中文 */ private static String longToChinese(long amount, boolean isUseTraditional) { - if(0 == amount){ + if (0 == amount) { return "零"; } @@ -235,11 +257,11 @@ public class NumberChineseFormatter { // 千 partValue = parts[0]; - if(partValue > 0){ + if (partValue > 0) { partChinese = thousandToChinese(partValue, isUseTraditional); chineseStr.insert(0, partChinese); - if(partValue < 1000){ + if (partValue < 1000) { // 和万位之间空0,则补零,如一万零三百 addPreZero(chineseStr); } @@ -247,26 +269,26 @@ public class NumberChineseFormatter { // 万 partValue = parts[1]; - if(partValue > 0){ - if((partValue % 10 == 0 && parts[0] > 0)){ + if (partValue > 0) { + if ((partValue % 10 == 0 && parts[0] > 0)) { // 如果"万"的个位是0,则补零,如十万零八千 addPreZero(chineseStr); } partChinese = thousandToChinese(partValue, isUseTraditional); chineseStr.insert(0, partChinese + "万"); - if(partValue < 1000){ + if (partValue < 1000) { // 和亿位之间空0,则补零,如一亿零三百万 addPreZero(chineseStr); } - } else{ + } else { addPreZero(chineseStr); } // 亿 partValue = parts[2]; - if(partValue > 0){ - if((partValue % 10 == 0 && parts[1] > 0)){ + if (partValue > 0) { + if ((partValue % 10 == 0 && parts[1] > 0)) { // 如果"万"的个位是0,则补零,如十万零八千 addPreZero(chineseStr); } @@ -274,25 +296,25 @@ public class NumberChineseFormatter { partChinese = thousandToChinese(partValue, isUseTraditional); chineseStr.insert(0, partChinese + "亿"); - if(partValue < 1000){ + if (partValue < 1000) { // 和万亿位之间空0,则补零,如一万亿零三百亿 addPreZero(chineseStr); } - } else{ + } else { addPreZero(chineseStr); } // 万亿 partValue = parts[3]; - if(partValue > 0){ - if(parts[2] == 0){ + if (partValue > 0) { + if (parts[2] == 0) { chineseStr.insert(0, "亿"); } partChinese = thousandToChinese(partValue, isUseTraditional); chineseStr.insert(0, partChinese + "万"); } - if(StrUtil.isNotEmpty(chineseStr) && '零' == chineseStr.charAt(0)){ + if (StrUtil.isNotEmpty(chineseStr) && '零' == chineseStr.charAt(0)) { return chineseStr.substring(1); } @@ -386,7 +408,7 @@ public class NumberChineseFormatter { } else { // 非节单位,和单位前的单数字组合为值 int unitNumber = number; - if(0 == number && 0 == i){ + if (0 == number && 0 == i) { // issue#1726,对于单位开头的数组,默认赋予1 // 十二 -> 一十二 // 百二 -> 一百二 @@ -502,12 +524,12 @@ public class NumberChineseFormatter { } } - private static void addPreZero(StringBuilder chineseStr){ - if(StrUtil.isEmpty(chineseStr)){ + private static void addPreZero(StringBuilder chineseStr) { + if (StrUtil.isEmpty(chineseStr)) { return; } final char c = chineseStr.charAt(0); - if('零' != c){ + if ('零' != c) { chineseStr.insert(0, '零'); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java index e0622f7fc..8cd3356f4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java @@ -700,7 +700,7 @@ public class FileUtil extends PathUtil { if (null == file) { return null; } - return mkdir(file.getParentFile()); + return mkdir(getParent(file, 1)); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java index fbdc4938f..3e7df8220 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileCopier.java @@ -267,8 +267,7 @@ public class FileCopier extends SrcToDestCopier{ } }else { //路径不存在则创建父目录 - //noinspection ResultOfMethodCallIgnored - dest.getParentFile().mkdirs(); + FileUtil.mkParentDirs(dest); } final ArrayList optionList = new ArrayList<>(2); diff --git a/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java b/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java index a35d78f1e..56eb04c46 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/NamingCase.java @@ -97,7 +97,7 @@ public class NamingCase { // 后一个为大写,按照专有名词对待,如aBC -> a_BC } else { //前一个为大写 - if (null == nextChar || Character.isLowerCase(nextChar)) { + if (null != nextChar && Character.isLowerCase(nextChar)) { // 普通首字母大写,如ABcc -> A_bcc sb.append(symbol); c = Character.toLowerCase(c); @@ -157,7 +157,7 @@ public class NamingCase { * 将连接符方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 * * @param name 转换前的自定义方式命名的字符串 - * @param symbol 连接符 + * @param symbol 原字符串中的连接符连接符 * @return 转换后的驼峰式命名的字符串 * @since 5.7.17 */ diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java index 84a244e37..2ac2f9186 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvBaseReader.java @@ -177,7 +177,7 @@ public class CsvBaseReader implements Serializable { final CsvParser csvParser = parse(reader); final List rows = new ArrayList<>(); read(csvParser, rows::add); - final List header = config.containsHeader ? csvParser.getHeader() : null; + final List header = config.headerLineNo > -1 ? csvParser.getHeader() : null; return new CsvData(header, rows); } diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java index 21d996039..690127f4f 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java @@ -84,13 +84,13 @@ public final class CsvParser extends ComputeIter implements Closeable, S } /** - * 获取头部字段列表,如果containsHeader设置为false则抛出异常 + * 获取头部字段列表,如果headerLineNo < 0,抛出异常 * * @return 头部列表 * @throws IllegalStateException 如果不解析头部或者没有调用nextRow()方法 */ public List getHeader() { - if (false == config.containsHeader) { + if (config.headerLineNo < 0) { throw new IllegalStateException("No header available - header parsing is disabled"); } if (lineNo < config.beginLineNo) { @@ -152,7 +152,7 @@ public final class CsvParser extends ComputeIter implements Closeable, S } //初始化标题 - if (config.containsHeader && null == header) { + if (lineNo == config.headerLineNo && null == header) { initHeader(currentFields); // 作为标题行后,此行跳过,下一行做为第一行 continue; diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvReadConfig.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvReadConfig.java index f9373f8fc..5ad7c7d85 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvReadConfig.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvReadConfig.java @@ -11,8 +11,8 @@ import java.io.Serializable; public class CsvReadConfig extends CsvConfig implements Serializable { private static final long serialVersionUID = 5396453565371560052L; - /** 是否首行做为标题行,默认false */ - protected boolean containsHeader; + /** 指定标题行号,-1表示无标题行 */ + protected long headerLineNo = -1; /** 是否跳过空白行,默认true */ protected boolean skipEmptyRows = true; /** 每行字段个数不同时是否抛出异常,默认false */ @@ -34,13 +34,26 @@ public class CsvReadConfig extends CsvConfig implements Serializa } /** - * 设置是否首行做为标题行,默认false + * 设置是否首行做为标题行,默认false
+ * 当设置为{@code true}时,默认标题行号是{@link #beginLineNo},{@code false}为-1,表示无行号 * * @param containsHeader 是否首行做为标题行,默认false * @return this + * @see #setHeaderLineNo(long) */ public CsvReadConfig setContainsHeader(boolean containsHeader) { - this.containsHeader = containsHeader; + return setHeaderLineNo(containsHeader ? beginLineNo : -1); + } + + /** + * 设置标题行行号,默认-1,表示无标题行
+ * + * @param headerLineNo 标题行行号,-1表示无标题行 + * @return this + * @since 5.7.23 + */ + public CsvReadConfig setHeaderLineNo(long headerLineNo) { + this.headerLineNo = headerLineNo; return this; } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java index 8c948e8a9..399fe571d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ArrayUtil.java @@ -368,6 +368,48 @@ public class ArrayUtil extends PrimitiveArrayUtil { } } + /** + * 将新元素插入到到已有数组中的某个位置
+ * 添加新元素会生成一个新数组或原有数组
+ * 如果插入位置为为负数,那么生成一个由插入元素顺序加已有数组顺序的新数组 + * + * @param 数组元素类型 + * @param buffer 已有数组 + * @param index 位置,大于长度追加,否则替换,<0表示从头部追加 + * @param values 新值 + * @return 新数组或原有数组 + * @since 5.7.23 + */ + @SuppressWarnings({"unchecked"}) + public static T[] replace(T[] buffer, int index, T... values) { + if(isEmpty(values)){ + return buffer; + } + if(isEmpty(buffer)){ + return values; + } + if (index < 0) { + // 从头部追加 + return insert(buffer, 0, values); + } + if (index >= buffer.length) { + // 超出长度,尾部追加 + return append(buffer, values); + } + + if (buffer.length >= values.length + index) { + System.arraycopy(values, 0, buffer, index, values.length); + return buffer; + } + + // 替换长度大于原数组长度,新建数组 + int newArrayLength = index + values.length; + final T[] result = newArray(buffer.getClass().getComponentType(), newArrayLength); + System.arraycopy(buffer, 0, result, 0, index); + System.arraycopy(values, 0, result, index, values.length); + return result; + } + /** * 将新元素插入到到已有数组中的某个位置
* 添加新元素会生成一个新的数组,不影响原数组
@@ -1540,7 +1582,7 @@ public class ArrayUtil extends PrimitiveArrayUtil { * @since 4.5.18 */ public static boolean isAllEmpty(Object... args) { - for (Object obj: args) { + for (Object obj : args) { if (false == ObjectUtil.isEmpty(obj)) { return false; } @@ -1758,10 +1800,10 @@ public class ArrayUtil extends PrimitiveArrayUtil { /** * 查找最后一个子数组的开始位置 * - * @param array 数组 + * @param array 数组 * @param endInclude 查找结束的位置(包含) - * @param subArray 子数组 - * @param 数组元素类型 + * @param subArray 子数组 + * @param 数组元素类型 * @return 最后一个子数组的开始位置,即子数字第一个元素在数组中的位置 * @since 5.4.8 */ diff --git a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java index 80c50288d..8419ca29d 100644 --- a/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/io/FileCopierTest.java @@ -1,45 +1,59 @@ package cn.hutool.core.io; +import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import cn.hutool.core.io.file.FileCopier; +import java.io.File; + /** * 文件拷贝单元测试 - * @author Looly * + * @author Looly */ public class FileCopierTest { - + @Test @Ignore public void dirCopyTest() { FileCopier copier = FileCopier.create("D:\\Java", "e:/eclipse/eclipse2.zip"); copier.copy(); } - + @Test @Ignore public void dirCopyTest2() { //测试带.的文件夹复制 FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp"); copier.copy(); - + FileUtil.copy("D:\\workspace\\java\\looly\\hutool\\.git", "D:\\workspace\\java\\temp", true); } - + @Test(expected = IORuntimeException.class) public void dirCopySubTest() { //测试父目录复制到子目录报错 FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp"); copier.copy(); } - + @Test @Ignore public void copyFileToDirTest() { FileCopier copier = FileCopier.create("d:/GReen_Soft/XshellXftpPortable.zip", "c:/hp/"); copier.copy(); } + + @Test + @Ignore + public void copyFileByRelativePath(){ + // https://github.com/dromara/hutool/pull/2188 + // 当复制的目标文件位置是相对路径的时候可以通过 + FileCopier copier = FileCopier.create(new File("pom.xml"),new File("aaa.txt")); + copier.copy(); + final boolean delete = new File("aaa.txt").delete(); + Assert.assertTrue(delete); + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java b/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java index 62545d934..2eec1a248 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/NamingCaseTest.java @@ -1,21 +1,44 @@ package cn.hutool.core.text; import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.CharUtil; import org.junit.Assert; import org.junit.Test; public class NamingCaseTest { + @Test - public void toUnderlineCaseTest(){ - // https://github.com/dromara/hutool/issues/2070 + public void toCamelCaseTest() { Dict.create() - .set("customerNickV2", "customer_nick_v2") - .forEach((key, value) -> Assert.assertEquals(value, NamingCase.toUnderlineCase(key))); + .set("Table_Test_Of_day","tableTestOfDay") + .set("TableTestOfDay","TableTestOfDay") + .set("abc_1d","abc1d") + .forEach((key, value) -> Assert.assertEquals(value, NamingCase.toCamelCase(key))); } @Test - public void toUnderLineCaseTest2(){ - final String wPRunOZTime = NamingCase.toUnderlineCase("wPRunOZTime"); - Assert.assertEquals("w_P_run_OZ_time", wPRunOZTime); + public void toCamelCaseFromDashedTest() { + Dict.create() + .set("Table-Test-Of-day","tableTestOfDay") + .forEach((key, value) -> Assert.assertEquals(value, NamingCase.toCamelCase(key, CharUtil.DASHED))); + } + + @Test + public void toUnderLineCaseTest() { + Dict.create() + .set("Table_Test_Of_day", "table_test_of_day") + .set("_Table_Test_Of_day_", "_table_test_of_day_") + .set("_Table_Test_Of_DAY_", "_table_test_of_DAY_") + .set("_TableTestOfDAYToday", "_table_test_of_DAY_today") + .set("HelloWorld_test", "hello_world_test") + .set("H2", "H2") + .set("H#case", "H#case") + .set("PNLabel", "PN_label") + .set("wPRunOZTime", "w_P_run_OZ_time") + // https://github.com/dromara/hutool/issues/2070 + .set("customerNickV2", "customer_nick_v2") + // https://gitee.com/dromara/hutool/issues/I4X9TT + .set("DEPT_NAME","DEPT_NAME") + .forEach((key, value) -> Assert.assertEquals(value, NamingCase.toUnderlineCase(key))); } } diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java index 3ba18becf..92f3603a8 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ArrayUtilTest.java @@ -363,7 +363,7 @@ public class ArrayUtilTest { } @Test - public void indexOfSubTest2(){ + public void indexOfSubTest2() { Integer[] a = {0x12, 0x56, 0x34, 0x56, 0x78, 0x9A}; Integer[] b = {0x56, 0x78}; int i = ArrayUtil.indexOfSub(a, b); @@ -401,7 +401,7 @@ public class ArrayUtilTest { } @Test - public void lastIndexOfSubTest2(){ + public void lastIndexOfSubTest2() { Integer[] a = {0x12, 0x56, 0x78, 0x56, 0x21, 0x9A}; Integer[] b = {0x56, 0x78}; int i = ArrayUtil.indexOfSub(a, b); @@ -409,17 +409,17 @@ public class ArrayUtilTest { } @Test - public void reverseTest(){ - int[] a = {1,2,3,4}; + public void reverseTest() { + int[] a = {1, 2, 3, 4}; final int[] reverse = ArrayUtil.reverse(a); - Assert.assertArrayEquals(new int[]{4,3,2,1}, reverse); + Assert.assertArrayEquals(new int[]{4, 3, 2, 1}, reverse); } @Test - public void reverseTest2s(){ - Object[] a = {"1",'2',"3",4}; + public void reverseTest2s() { + Object[] a = {"1", '2', "3", 4}; final Object[] reverse = ArrayUtil.reverse(a); - Assert.assertArrayEquals(new Object[]{4,"3",'2',"1"}, reverse); + Assert.assertArrayEquals(new Object[]{4, "3", '2', "1"}, reverse); } @Test @@ -461,9 +461,57 @@ public class ArrayUtilTest { } @Test - public void getTest(){ + public void getTest() { String[] a = {"a", "b", "c"}; final Object o = ArrayUtil.get(a, -1); Assert.assertEquals("c", o); } + + @Test + public void replaceTest() { + String[] a = {"1", "2", "3", "4"}; + String[] b = {"a", "b", "c"}; + + // 在小于0的位置,-1位置插入,返回b+a,新数组 + String[] result = ArrayUtil.replace(a, -1, b); + Assert.assertArrayEquals(new String[]{"a", "b", "c", "1", "2", "3", "4"}, result); + + // 在第0个位置开始替换,返回a + result = ArrayUtil.replace(ArrayUtil.clone(a), 0, b); + Assert.assertArrayEquals(new String[]{"a", "b", "c", "4"}, result); + + // 在第1个位置替换,即"2"开始 + result = ArrayUtil.replace(ArrayUtil.clone(a), 1, b); + Assert.assertArrayEquals(new String[]{"1", "a", "b", "c"}, result); + + // 在第2个位置插入,即"3"之后 + result = ArrayUtil.replace(ArrayUtil.clone(a), 2, b); + Assert.assertArrayEquals(new String[]{"1", "2", "a", "b", "c"}, result); + + // 在第3个位置插入,即"4"之后 + result = ArrayUtil.replace(ArrayUtil.clone(a), 3, b); + Assert.assertArrayEquals(new String[]{"1", "2", "3", "a", "b", "c"}, result); + + // 在第4个位置插入,数组长度为4,在索引4出替换即两个数组相加 + result = ArrayUtil.replace(ArrayUtil.clone(a), 4, b); + Assert.assertArrayEquals(new String[]{"1", "2", "3", "4", "a", "b", "c"}, result); + + // 在大于3个位置插入,数组长度为4,即两个数组相加 + result = ArrayUtil.replace(ArrayUtil.clone(a), 5, b); + Assert.assertArrayEquals(new String[]{"1", "2", "3", "4", "a", "b", "c"}, result); + + String[] e = null; + String[] f = {"a", "b", "c"}; + + // e为null 返回 f + result = ArrayUtil.replace(e, -1, f); + Assert.assertArrayEquals(f, result); + + String[] g = {"a", "b", "c"}; + String[] h = null; + + // h为null 返回 g + result = ArrayUtil.replace(g, 0, h); + Assert.assertArrayEquals(g, result); + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java index ee1e1da50..514efd089 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java @@ -391,40 +391,6 @@ public class StrUtilTest { Assert.assertEquals(text, str); } - @Test - public void toCamelCaseTest() { - String str = "Table_Test_Of_day"; - String result = StrUtil.toCamelCase(str); - Assert.assertEquals("tableTestOfDay", result); - - String str1 = "TableTestOfDay"; - String result1 = StrUtil.toCamelCase(str1); - Assert.assertEquals("TableTestOfDay", result1); - - String abc1d = StrUtil.toCamelCase("abc_1d"); - Assert.assertEquals("abc1d", abc1d); - - - String str2 = "Table-Test-Of-day"; - String result2 = StrUtil.toCamelCase(str2, CharUtil.DASHED); - System.out.println(result2); - Assert.assertEquals("tableTestOfDay", result2); - } - - @Test - public void toUnderLineCaseTest() { - Dict.create() - .set("Table_Test_Of_day", "table_test_of_day") - .set("_Table_Test_Of_day_", "_table_test_of_day_") - .set("_Table_Test_Of_DAY_", "_table_test_of_DAY_") - .set("_TableTestOfDAYToday", "_table_test_of_DAY_today") - .set("HelloWorld_test", "hello_world_test") - .set("H2", "H2") - .set("H#case", "H#case") - .set("PNLabel", "PN_label") - .forEach((key, value) -> Assert.assertEquals(value, StrUtil.toUnderlineCase(key))); - } - @Test public void containsAnyTest() { //字符 diff --git a/hutool-db/pom.xml b/hutool-db/pom.xml index f14a0cc1b..ed7e1aed5 100644 --- a/hutool-db/pom.xml +++ b/hutool-db/pom.xml @@ -20,10 +20,9 @@ 0.9.5.5 2.9.0 - 10.0.14 + 10.0.16 1.2.8 2.4.13 - 3.12.10 4.5.0 3.36.0.3 @@ -97,13 +96,6 @@ ${dbcp2.version} true - - - org.mongodb - mongo-java-driver - ${mongo.version} - true - org.mongodb mongodb-driver-sync diff --git a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java index 8158f0d33..65ce4fa7a 100644 --- a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java +++ b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS.java @@ -6,29 +6,35 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.db.DbRuntimeException; import cn.hutool.log.Log; import cn.hutool.setting.Setting; -import com.mongodb.MongoClient; -import com.mongodb.MongoClientOptions; -import com.mongodb.MongoClientOptions.Builder; +import com.mongodb.MongoClientSettings; import com.mongodb.MongoCredential; import com.mongodb.ServerAddress; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; +import com.mongodb.connection.ConnectionPoolSettings; +import com.mongodb.connection.SocketSettings; import org.bson.Document; import java.io.Closeable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; /** - * MongoDB工具类 - * - * @author xiaoleilu + * MongoDB4工具类 * + * @author VampireAchao */ public class MongoDS implements Closeable { + private final static Log log = Log.get(); - /** 默认配置文件 */ + /** + * 默认配置文件 + */ public final static String MONGO_CONFIG_PATH = "config/mongo.setting"; // MongoDB配置文件 @@ -41,6 +47,7 @@ public class MongoDS implements Closeable { private MongoClient mongo; // --------------------------------------------------------------------------- Constructor start + /** * 构造MongoDB数据源
* 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! @@ -58,8 +65,8 @@ public class MongoDS implements Closeable { * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! * * @param mongoSetting MongoDB的配置文件,如果是null则读取默认配置文件或者使用MongoDB默认客户端配置 - * @param host 主机(域名或者IP) - * @param port 端口 + * @param host 主机(域名或者IP) + * @param port 端口 */ public MongoDS(Setting mongoSetting, String host, int port) { this.setting = mongoSetting; @@ -86,7 +93,7 @@ public class MongoDS implements Closeable { * 官方文档: http://docs.mongodb.org/manual/administration/replica-sets/ * * @param mongoSetting MongoDB的配置文件,必须有 - * @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式 + * @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式 */ public MongoDS(Setting mongoSetting, String... groups) { if (mongoSetting == null) { @@ -146,11 +153,13 @@ public class MongoDS implements Closeable { final MongoCredential credentail = createCredentail(group); try { - if (null == credentail) { - mongo = new MongoClient(serverAddress, buildMongoClientOptions(group)); - } else { - mongo = new MongoClient(serverAddress, credentail, buildMongoClientOptions(group)); + MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder() + .applyToClusterSettings(b -> b.hosts(Collections.singletonList(serverAddress))); + buildMongoClientSettings(clusterSettingsBuilder, group); + if (null != credentail) { + clusterSettingsBuilder.credential(credentail); } + mongo = MongoClients.create(clusterSettingsBuilder.build()); } catch (Exception e) { throw new DbRuntimeException(StrUtil.format("Init MongoDB pool with connection to [{}] error!", serverAddress), e); } @@ -192,11 +201,13 @@ public class MongoDS implements Closeable { final MongoCredential credentail = createCredentail(StrUtil.EMPTY); try { - if (null == credentail) { - mongo = new MongoClient(addrList, buildMongoClientOptions(StrUtil.EMPTY)); - } else { - mongo = new MongoClient(addrList, credentail, buildMongoClientOptions(StrUtil.EMPTY)); + MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder() + .applyToClusterSettings(b -> b.hosts(addrList)); + buildMongoClientSettings(clusterSettingsBuilder, StrUtil.EMPTY); + if (null != credentail) { + clusterSettingsBuilder.credential(credentail); } + mongo = MongoClients.create(clusterSettingsBuilder.build()); } catch (Exception e) { log.error(e, "Init MongoDB connection error!"); return; @@ -234,7 +245,7 @@ public class MongoDS implements Closeable { /** * 获得MongoDB中指定集合对象 * - * @param dbName 库名 + * @param dbName 库名 * @param collectionName 集合名 * @return DBCollection */ @@ -248,6 +259,7 @@ public class MongoDS implements Closeable { } // --------------------------------------------------------------------------- Private method start + /** * 创建ServerAddress对象,会读取配置文件中的相关信息 * @@ -291,7 +303,7 @@ public class MongoDS implements Closeable { */ private MongoCredential createCredentail(String group) { final Setting setting = this.setting; - if(null == setting) { + if (null == setting) { return null; } final String user = setting.getStr("user", group, setting.getStr("user")); @@ -316,23 +328,13 @@ public class MongoDS implements Closeable { return MongoCredential.createCredential(userName, database, password.toCharArray()); } - /** - * 构件MongoDB连接选项
- * - * @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值 - * @return MongoClientOptions - */ - private MongoClientOptions buildMongoClientOptions(String group) { - return buildMongoClientOptions(MongoClientOptions.builder(), group).build(); - } - /** * 构件MongoDB连接选项
* * @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值 * @return Builder */ - private Builder buildMongoClientOptions(Builder builder, String group) { + private MongoClientSettings.Builder buildMongoClientSettings(MongoClientSettings.Builder builder, String group) { if (setting == null) { return builder; } @@ -348,8 +350,9 @@ public class MongoDS implements Closeable { if (StrUtil.isBlank(group) == false && connectionsPerHost == null) { connectionsPerHost = setting.getInt("connectionsPerHost"); } + ConnectionPoolSettings.Builder connectionPoolSettingsBuilder = ConnectionPoolSettings.builder(); if (connectionsPerHost != null) { - builder.connectionsPerHost(connectionsPerHost); + connectionPoolSettingsBuilder.maxSize(connectionsPerHost); log.debug("MongoDB connectionsPerHost: {}", connectionsPerHost); } @@ -359,9 +362,10 @@ public class MongoDS implements Closeable { setting.getInt("connectTimeout"); } if (connectTimeout != null) { - builder.connectTimeout(connectTimeout); + connectionPoolSettingsBuilder.maxWaitTime(connectTimeout, TimeUnit.MILLISECONDS); log.debug("MongoDB connectTimeout: {}", connectTimeout); } + builder.applyToConnectionPoolSettings(b -> b.applySettings(connectionPoolSettingsBuilder.build())); // 套接字超时时间;该值会被传递给Socket.setSoTimeout(int)。默以为0(无穷) --int Integer socketTimeout = setting.getInt(group + "socketTimeout"); @@ -369,7 +373,8 @@ public class MongoDS implements Closeable { setting.getInt("socketTimeout"); } if (socketTimeout != null) { - builder.socketTimeout(socketTimeout); + SocketSettings socketSettings = SocketSettings.builder().connectTimeout(socketTimeout, TimeUnit.MILLISECONDS).build(); + builder.applyToSocketSettings(b -> b.applySettings(socketSettings)); log.debug("MongoDB socketTimeout: {}", socketTimeout); } @@ -388,4 +393,5 @@ public class MongoDS implements Closeable { return this.setting; } // --------------------------------------------------------------------------- Private method end + } diff --git a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS4.java b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS4.java deleted file mode 100644 index 0b2ff0f62..000000000 --- a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoDS4.java +++ /dev/null @@ -1,404 +0,0 @@ -package cn.hutool.db.nosql.mongo; - -import cn.hutool.core.exceptions.NotInitedException; -import cn.hutool.core.net.NetUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.db.DbRuntimeException; -import cn.hutool.log.Log; -import cn.hutool.setting.Setting; -import com.mongodb.MongoClientSettings; -import com.mongodb.MongoCredential; -import com.mongodb.MongoDriverInformation; -import com.mongodb.ServerAddress; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.internal.MongoClientImpl; -import com.mongodb.connection.ConnectionPoolSettings; -import com.mongodb.connection.SocketSettings; -import org.bson.Document; - -import java.io.Closeable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -/** - * MongoDB4工具类 - * - * @author VampireAchao - */ -public class MongoDS4 implements Closeable { - - private final static Log log = Log.get(); - - /** - * 默认配置文件 - */ - public final static String MONGO_CONFIG_PATH = "config/mongo.setting"; - - // MongoDB配置文件 - private Setting setting; - // MongoDB实例连接列表 - private String[] groups; - // MongoDB单点连接信息 - private ServerAddress serverAddress; - // MongoDB客户端对象 - private MongoClient mongo; - - // --------------------------------------------------------------------------- Constructor start - - /** - * 构造MongoDB数据源
- * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! - * - * @param host 主机(域名或者IP) - * @param port 端口 - */ - public MongoDS4(String host, int port) { - this.serverAddress = createServerAddress(host, port); - initSingle(); - } - - /** - * 构造MongoDB数据源
- * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! - * - * @param mongoSetting MongoDB的配置文件,如果是null则读取默认配置文件或者使用MongoDB默认客户端配置 - * @param host 主机(域名或者IP) - * @param port 端口 - */ - public MongoDS4(Setting mongoSetting, String host, int port) { - this.setting = mongoSetting; - this.serverAddress = createServerAddress(host, port); - initSingle(); - } - - /** - * 构造MongoDB数据源
- * 当提供多个数据源时,这些数据源将为一个副本集或者多个mongos
- * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! 官方文档: http://docs.mongodb.org/manual/administration/replica-sets/ - * - * @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式 - */ - public MongoDS4(String... groups) { - this.groups = groups; - init(); - } - - /** - * 构造MongoDB数据源
- * 当提供多个数据源时,这些数据源将为一个副本集或者mongos
- * 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败!
- * 官方文档: http://docs.mongodb.org/manual/administration/replica-sets/ - * - * @param mongoSetting MongoDB的配置文件,必须有 - * @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式 - */ - public MongoDS4(Setting mongoSetting, String... groups) { - if (mongoSetting == null) { - throw new DbRuntimeException("Mongo setting is null!"); - } - this.setting = mongoSetting; - this.groups = groups; - init(); - } - // --------------------------------------------------------------------------- Constructor end - - /** - * 初始化,当给定分组数大于一个时使用 - */ - public void init() { - if (groups != null && groups.length > 1) { - initCloud(); - } else { - initSingle(); - } - } - - /** - * 初始化
- * 设定文件中的host和端口有三种形式: - * - *

-	 * host = host:port
-	 * 
- * - *
-	 * host = host
-	 * port = port
-	 * 
- * - *
-	 * host = host
-	 * 
- */ - synchronized public void initSingle() { - if (setting == null) { - try { - setting = new Setting(MONGO_CONFIG_PATH, true); - } catch (Exception e) { - // 在single模式下,可以没有配置文件。 - } - } - - String group = StrUtil.EMPTY; - if (null == this.serverAddress) { - //存在唯一分组 - if (groups != null && groups.length == 1) { - group = groups[0]; - } - serverAddress = createServerAddress(group); - } - - final MongoCredential credentail = createCredentail(group); - try { - MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder().applyToClusterSettings(b -> b.hosts(Collections.singletonList(serverAddress))); - if (null != credentail) { - clusterSettingsBuilder.credential(credentail); - } - mongo = new MongoClientImpl(clusterSettingsBuilder.build(), MongoDriverInformation.builder().build()); - } catch (Exception e) { - throw new DbRuntimeException(StrUtil.format("Init MongoDB pool with connection to [{}] error!", serverAddress), e); - } - - log.info("Init MongoDB pool with connection to [{}]", serverAddress); - } - - /** - * 初始化集群
- * 集群的其它客户端设定参数使用全局设定
- * 集群中每一个实例成员用一个group表示,例如: - * - *
-	 * user = test1
-	 * pass = 123456
-	 * database = test
-	 * [db0]
-	 * host = 192.168.1.1:27117
-	 * [db1]
-	 * host = 192.168.1.1:27118
-	 * [db2]
-	 * host = 192.168.1.1:27119
-	 * 
- */ - synchronized public void initCloud() { - if (groups == null || groups.length == 0) { - throw new DbRuntimeException("Please give replication set groups!"); - } - - if (setting == null) { - // 若未指定配置文件,则使用默认配置文件 - setting = new Setting(MONGO_CONFIG_PATH, true); - } - - final List addrList = new ArrayList<>(); - for (String group : groups) { - addrList.add(createServerAddress(group)); - } - - final MongoCredential credentail = createCredentail(StrUtil.EMPTY); - try { - MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder().applyToClusterSettings(b -> b.hosts(addrList)); - if (null != credentail) { - clusterSettingsBuilder.credential(credentail); - } - mongo = new MongoClientImpl(clusterSettingsBuilder.build(), MongoDriverInformation.builder().build()); - } catch (Exception e) { - log.error(e, "Init MongoDB connection error!"); - return; - } - - log.info("Init MongoDB cloud Set pool with connection to {}", addrList); - } - - /** - * 设定MongoDB配置文件 - * - * @param setting 配置文件 - */ - public void setSetting(Setting setting) { - this.setting = setting; - } - - /** - * @return 获得MongoDB客户端对象 - */ - public MongoClient getMongo() { - return mongo; - } - - /** - * 获得DB - * - * @param dbName DB - * @return DB - */ - public MongoDatabase getDb(String dbName) { - return mongo.getDatabase(dbName); - } - - /** - * 获得MongoDB中指定集合对象 - * - * @param dbName 库名 - * @param collectionName 集合名 - * @return DBCollection - */ - public MongoCollection getCollection(String dbName, String collectionName) { - return getDb(dbName).getCollection(collectionName); - } - - @Override - public void close() { - mongo.close(); - } - - // --------------------------------------------------------------------------- Private method start - - /** - * 创建ServerAddress对象,会读取配置文件中的相关信息 - * - * @param group 分组,如果为{@code null}或者""默认为无分组 - * @return ServerAddress - */ - private ServerAddress createServerAddress(String group) { - final Setting setting = checkSetting(); - - if (group == null) { - group = StrUtil.EMPTY; - } - - final String tmpHost = setting.getByGroup("host", group); - if (StrUtil.isBlank(tmpHost)) { - throw new NotInitedException("Host name is empy of group: {}", group); - } - - final int defaultPort = setting.getInt("port", group, 27017); - return new ServerAddress(NetUtil.buildInetSocketAddress(tmpHost, defaultPort)); - } - - /** - * 创建ServerAddress对象 - * - * @param host 主机域名或者IP(如果为空默认127.0.0.1) - * @param port 端口(如果为空默认为) - * @return ServerAddress - */ - private ServerAddress createServerAddress(String host, int port) { - return new ServerAddress(host, port); - } - - /** - * 创建{@link MongoCredential},用于服务端验证
- * 此方法会首先读取指定分组下的属性,用户没有定义则读取空分组下的属性 - * - * @param group 分组 - * @return {@link MongoCredential},如果用户未指定用户名密码返回null - * @since 4.1.20 - */ - private MongoCredential createCredentail(String group) { - final Setting setting = this.setting; - if (null == setting) { - return null; - } - final String user = setting.getStr("user", group, setting.getStr("user")); - final String pass = setting.getStr("pass", group, setting.getStr("pass")); - final String database = setting.getStr("database", group, setting.getStr("database")); - return createCredentail(user, database, pass); - } - - /** - * 创建{@link MongoCredential},用于服务端验证 - * - * @param userName 用户名 - * @param database 数据库名 - * @param password 密码 - * @return {@link MongoCredential} - * @since 4.1.20 - */ - private MongoCredential createCredentail(String userName, String database, String password) { - if (StrUtil.hasEmpty(userName, database, database)) { - return null; - } - return MongoCredential.createCredential(userName, database, password.toCharArray()); - } - - /** - * 构件MongoDB连接选项
- * - * @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值 - * @return MongoClientOptions - */ - private MongoClientSettings buildMongoClientOptions(String group) { - return buildMongoClientOptions(MongoClientSettings.builder(), group).build(); - } - - /** - * 构件MongoDB连接选项
- * - * @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值 - * @return Builder - */ - private MongoClientSettings.Builder buildMongoClientOptions(MongoClientSettings.Builder builder, String group) { - if (setting == null) { - return builder; - } - - if (StrUtil.isEmpty(group)) { - group = StrUtil.EMPTY; - } else { - group = group + StrUtil.DOT; - } - - // 每个主机答应的连接数(每个主机的连接池大小),当连接池被用光时,会被阻塞住 - Integer connectionsPerHost = setting.getInt(group + "connectionsPerHost"); - if (StrUtil.isBlank(group) == false && connectionsPerHost == null) { - connectionsPerHost = setting.getInt("connectionsPerHost"); - } - ConnectionPoolSettings.Builder connectionPoolSettingsBuilder = ConnectionPoolSettings.builder(); - if (connectionsPerHost != null) { - connectionPoolSettingsBuilder.maxSize(connectionsPerHost); - log.debug("MongoDB connectionsPerHost: {}", connectionsPerHost); - } - - // 被阻塞线程从连接池获取连接的最长等待时间(ms) --int - Integer connectTimeout = setting.getInt(group + "connectTimeout"); - if (StrUtil.isBlank(group) == false && connectTimeout == null) { - setting.getInt("connectTimeout"); - } - if (connectTimeout != null) { - connectionPoolSettingsBuilder.maxWaitTime(connectTimeout, TimeUnit.MILLISECONDS); - log.debug("MongoDB connectTimeout: {}", connectTimeout); - } - builder.applyToConnectionPoolSettings(b -> b.applySettings(connectionPoolSettingsBuilder.build())); - - // 套接字超时时间;该值会被传递给Socket.setSoTimeout(int)。默以为0(无穷) --int - Integer socketTimeout = setting.getInt(group + "socketTimeout"); - if (StrUtil.isBlank(group) == false && socketTimeout == null) { - setting.getInt("socketTimeout"); - } - if (socketTimeout != null) { - SocketSettings socketSettings = SocketSettings.builder().connectTimeout(socketTimeout, TimeUnit.MILLISECONDS).build(); - builder.applyToSocketSettings(b -> b.applySettings(socketSettings)); - log.debug("MongoDB socketTimeout: {}", socketTimeout); - } - - return builder; - } - - /** - * 检查Setting配置文件 - * - * @return Setting配置文件 - */ - private Setting checkSetting() { - if (null == this.setting) { - throw new DbRuntimeException("Please indicate setting file or create default [{}]", MONGO_CONFIG_PATH); - } - return this.setting; - } - // --------------------------------------------------------------------------- Private method end - -} diff --git a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java index 23fdf8aad..25717bcb7 100644 --- a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java +++ b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory.java @@ -10,16 +10,20 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** - * MongoDB工厂类,用于创建 - * @author looly + * {@link MongoDS}工厂类,用于创建 * + * @author Looly, VampireAchao */ public class MongoFactory { - /** 各分组做组合key的时候分隔符 */ + /** + * 各分组做组合key的时候分隔符 + */ private final static String GROUP_SEPRATER = ","; - /** 数据源池 */ + /** + * 数据源池 + */ private static final Map DS_MAP = new ConcurrentHashMap<>(); // JVM关闭前关闭MongoDB连接 @@ -28,6 +32,7 @@ public class MongoFactory { } // ------------------------------------------------------------------------ Get DS start + /** * 获取MongoDB数据源
* @@ -80,7 +85,7 @@ public class MongoFactory { * 获取MongoDB数据源
* * @param setting 设定文件 - * @param groups 分组列表 + * @param groups 分组列表 * @return MongoDB连接 */ public static MongoDS getDS(Setting setting, String... groups) { @@ -99,7 +104,7 @@ public class MongoFactory { * 获取MongoDB数据源
* * @param setting 配置文件 - * @param groups 分组列表 + * @param groups 分组列表 * @return MongoDB连接 */ public static MongoDS getDS(Setting setting, Collection groups) { @@ -111,8 +116,8 @@ public class MongoFactory { * 关闭全部连接 */ public static void closeAll() { - if(MapUtil.isNotEmpty(DS_MAP)){ - for(MongoDS ds : DS_MAP.values()) { + if (MapUtil.isNotEmpty(DS_MAP)) { + for (MongoDS ds : DS_MAP.values()) { ds.close(); } DS_MAP.clear(); diff --git a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory4.java b/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory4.java deleted file mode 100644 index a099118a4..000000000 --- a/hutool-db/src/main/java/cn/hutool/db/nosql/mongo/MongoFactory4.java +++ /dev/null @@ -1,120 +0,0 @@ -package cn.hutool.db.nosql.mongo; - -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.RuntimeUtil; -import cn.hutool.setting.Setting; - -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * MongoDB4工厂类,用于创建 - * @author VampireAchao - */ -public class MongoFactory4 { - - /** 各分组做组合key的时候分隔符 */ - private final static String GROUP_SEPRATER = ","; - - /** 数据源池 */ - private static final Map DS_MAP = new ConcurrentHashMap<>(); - - // JVM关闭前关闭MongoDB连接 - static { - RuntimeUtil.addShutdownHook(MongoFactory4::closeAll); - } - - // ------------------------------------------------------------------------ Get DS start - /** - * 获取MongoDB数据源
- * - * @param host 主机 - * @param port 端口 - * @return MongoDB连接 - */ - public static MongoDS4 getDS(String host, int port) { - final String key = host + ":" + port; - MongoDS4 ds = DS_MAP.get(key); - if (null == ds) { - // 没有在池中加入之 - ds = new MongoDS4(host, port); - DS_MAP.put(key, ds); - } - - return ds; - } - - /** - * 获取MongoDB数据源
- * 多个分组名对应的连接组成集群 - * - * @param groups 分组列表 - * @return MongoDB连接 - */ - public static MongoDS4 getDS(String... groups) { - final String key = ArrayUtil.join(groups, GROUP_SEPRATER); - MongoDS4 ds = DS_MAP.get(key); - if (null == ds) { - // 没有在池中加入之 - ds = new MongoDS4(groups); - DS_MAP.put(key, ds); - } - - return ds; - } - - /** - * 获取MongoDB数据源
- * - * @param groups 分组列表 - * @return MongoDB连接 - */ - public static MongoDS4 getDS(Collection groups) { - return getDS(groups.toArray(new String[0])); - } - - /** - * 获取MongoDB数据源
- * - * @param setting 设定文件 - * @param groups 分组列表 - * @return MongoDB连接 - */ - public static MongoDS4 getDS(Setting setting, String... groups) { - final String key = setting.getSettingPath() + GROUP_SEPRATER + ArrayUtil.join(groups, GROUP_SEPRATER); - MongoDS4 ds = DS_MAP.get(key); - if (null == ds) { - // 没有在池中加入之 - ds = new MongoDS4(setting, groups); - DS_MAP.put(key, ds); - } - - return ds; - } - - /** - * 获取MongoDB数据源
- * - * @param setting 配置文件 - * @param groups 分组列表 - * @return MongoDB连接 - */ - public static MongoDS4 getDS(Setting setting, Collection groups) { - return getDS(setting, groups.toArray(new String[0])); - } - // ------------------------------------------------------------------------ Get DS ends - - /** - * 关闭全部连接 - */ - public static void closeAll() { - if(MapUtil.isNotEmpty(DS_MAP)){ - for(MongoDS4 ds : DS_MAP.values()) { - ds.close(); - } - DS_MAP.clear(); - } - } -} diff --git a/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java b/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java index 67ffadd9f..866ba0b4c 100644 --- a/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java +++ b/hutool-db/src/test/java/cn/hutool/db/nosql/MongoDBTest.java @@ -1,6 +1,6 @@ package cn.hutool.db.nosql; -import cn.hutool.db.nosql.mongo.MongoFactory4; +import cn.hutool.db.nosql.mongo.MongoFactory; import com.mongodb.client.MongoDatabase; import org.junit.Assert; import org.junit.Ignore; @@ -13,8 +13,8 @@ public class MongoDBTest { @Test @Ignore - public void redisDSTest() { - MongoDatabase db = MongoFactory4.getDS("master").getDb("test"); + public void mongoDSTest() { + MongoDatabase db = MongoFactory.getDS("master").getDb("test"); Assert.assertEquals("test", db.getName()); } } diff --git a/hutool-db/src/test/resources/config/mongo.setting b/hutool-db/src/test/resources/config/mongo.setting index dc5ae2b33..b50b4d026 100644 --- a/hutool-db/src/test/resources/config/mongo.setting +++ b/hutool-db/src/test/resources/config/mongo.setting @@ -13,8 +13,8 @@ socketKeepAlive=false #---------------------------------- MongoDB实例连接 [master] -host = 127.0.0.1:27017 +host = localhost:27017 [slave] -host = 127.0.0.1:27018 +host = localhost:27018 #----------------------------------------------------- diff --git a/hutool-extra/src/main/resources/META-INF/spring.factories b/hutool-extra/src/main/resources/META-INF/spring.factories index 7b08ebdf8..09fa11dde 100644 --- a/hutool-extra/src/main/resources/META-INF/spring.factories +++ b/hutool-extra/src/main/resources/META-INF/spring.factories @@ -1,3 +1,3 @@ # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -cn.hutool.extra.spring.SpringUtil \ No newline at end of file +cn.hutool.extra.spring.SpringUtil diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java index 927c8bd4d..73883f0c7 100644 --- a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java @@ -1161,10 +1161,11 @@ public class HttpRequest extends HttpBase { /** * 对于GET请求将参数加到URL中
- * 此处不对URL中的特殊字符做单独编码 + * 此处不对URL中的特殊字符做单独编码
+ * 对于非rest的GET请求,且处于重定向时,参数丢弃 */ private void urlWithParamIfGet() { - if (Method.GET.equals(method) && false == this.isRest) { + if (Method.GET.equals(method) && false == this.isRest && this.redirectCount > 0) { // 优先使用body形式的参数,不存在使用form if (ArrayUtil.isNotEmpty(this.bodyBytes)) { this.url.getQuery().parse(StrUtil.str(this.bodyBytes, this.charset), this.charset); @@ -1194,7 +1195,7 @@ public class HttpRequest extends HttpBase { if (responseCode != HttpURLConnection.HTTP_OK) { if (HttpStatus.isRedirected(responseCode)) { - setUrl(httpConnection.header(Header.LOCATION)); + setUrl(UrlBuilder.ofHttpWithoutEncode(httpConnection.header(Header.LOCATION))); if (redirectCount < this.maxRedirectCount) { redirectCount++; // 重定向不再走过滤器 diff --git a/hutool-json/src/test/java/cn/hutool/json/IssueI4XFMWTest.java b/hutool-json/src/test/java/cn/hutool/json/IssueI4XFMWTest.java new file mode 100644 index 000000000..51cffe12c --- /dev/null +++ b/hutool-json/src/test/java/cn/hutool/json/IssueI4XFMWTest.java @@ -0,0 +1,42 @@ +package cn.hutool.json; + +import cn.hutool.core.annotation.Alias; +import lombok.Data; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://gitee.com/dromara/hutool/issues/I4XFMW + */ +public class IssueI4XFMWTest { + + @Test + public void test() { + List entityList = new ArrayList<>(); + TestEntity entityA = new TestEntity(); + entityA.setId("123"); + entityA.setPassword("456"); + entityList.add(entityA); + + TestEntity entityB = new TestEntity(); + entityB.setId("789"); + entityB.setPassword("098"); + entityList.add(entityB); + + String jsonStr = JSONUtil.toJsonStr(entityList); + Assert.assertEquals("[{\"uid\":\"123\",\"password\":\"456\"},{\"uid\":\"789\",\"password\":\"098\"}]", jsonStr); + List testEntities = JSONUtil.toList(jsonStr, TestEntity.class); + Assert.assertEquals("123", testEntities.get(0).getId()); + Assert.assertEquals("789", testEntities.get(1).getId()); + } + + @Data + static class TestEntity { + @Alias("uid") + private String id; + private String password; + } +}