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
+ * 当设置为{@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
* 添加新元素会生成一个新的数组,不影响原数组
@@ -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
* 调用者必须持有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
- * 此方法会首先读取指定分组下的属性,用户没有定义则读取空分组下的属性
- *
- * @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
*
@@ -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
- *
- * @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
- *
- * @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
- * 此处不对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