mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
Merge remote-tracking branch 'origin/v5-dev' into v5-dev
This commit is contained in:
commit
e1de291063
@ -2,7 +2,7 @@
|
|||||||
# 🚀Changelog
|
# 🚀Changelog
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
# 5.7.23 (2022-03-09)
|
# 5.7.23 (2022-03-15)
|
||||||
|
|
||||||
### 🐣新特性
|
### 🐣新特性
|
||||||
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
|
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
|
||||||
@ -11,11 +11,18 @@
|
|||||||
* 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee)
|
* 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee)
|
||||||
* 【core 】 增加Table实现(issue#2179@Github)
|
* 【core 】 增加Table实现(issue#2179@Github)
|
||||||
* 【core 】 增加UniqueKeySet(issue#I4WUWR@Gitee)
|
* 【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修复
|
### 🐞Bug修复
|
||||||
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
|
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
|
||||||
* 【core 】 修复NumberConverter对数字转换的问题(issue#I4WPF4@Gitee)
|
* 【core 】 修复NumberConverter对数字转换的问题(issue#I4WPF4@Gitee)
|
||||||
* 【core 】 修复ReflectUtil.getMethods获取接口方法问题(issue#I4WUWR@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)
|
# 5.7.22 (2022-03-01)
|
||||||
|
@ -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 documentation](https://www.hutool.cn/docs/)
|
||||||
|
|
||||||
|
[📘Chinese back-up documentation](https://plus.hutool.cn/docs/#/)
|
||||||
|
|
||||||
[📙API](https://apidoc.gitee.com/dromara/hutool/)
|
[📙API](https://apidoc.gitee.com/dromara/hutool/)
|
||||||
|
|
||||||
[🎬Video](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2)
|
[🎬Video](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2)
|
||||||
|
@ -107,6 +107,8 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
|
|||||||
|
|
||||||
[📘中文文档](https://www.hutool.cn/docs/)
|
[📘中文文档](https://www.hutool.cn/docs/)
|
||||||
|
|
||||||
|
[📘中文备用文档](https://plus.hutool.cn/docs/#/)
|
||||||
|
|
||||||
[📙参考API](https://apidoc.gitee.com/dromara/hutool/)
|
[📙参考API](https://apidoc.gitee.com/dromara/hutool/)
|
||||||
|
|
||||||
[🎬视频介绍](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2)
|
[🎬视频介绍](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2)
|
||||||
|
@ -40,7 +40,6 @@ public class NumberChineseFormatter {
|
|||||||
new ChineseUnit('亿', 1_0000_0000, true),
|
new ChineseUnit('亿', 1_0000_0000, true),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
|
* 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
|
||||||
*
|
*
|
||||||
@ -53,15 +52,26 @@ public class NumberChineseFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
|
* 阿拉伯数字转换成中文.
|
||||||
|
*
|
||||||
|
* <p>主要是对发票票面金额转换的扩展
|
||||||
|
* <p>如:-12.32
|
||||||
|
* <p>发票票面转换为:(负数)壹拾贰圆叁角贰分
|
||||||
|
* <p>而非:负壹拾贰元叁角贰分
|
||||||
|
* <p>共两点不同:1、(负数) 而非 负;2、圆 而非 元
|
||||||
|
* 2022/3/9
|
||||||
*
|
*
|
||||||
* @param amount 数字
|
* @param amount 数字
|
||||||
* @param isUseTraditional 是否使用繁体
|
* @param isUseTraditional 是否使用繁体
|
||||||
* @param isMoneyMode 是否为金额模式
|
* @param isMoneyMode 是否金额模式
|
||||||
* @return 中文
|
* @param negativeName 负号转换名称 如:负、(负数)
|
||||||
|
* @param unitName 单位名称 如:元、圆
|
||||||
|
* @return java.lang.String
|
||||||
|
* @author machuanpeng
|
||||||
|
* @since 5.7.23
|
||||||
*/
|
*/
|
||||||
public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode) {
|
public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode, String negativeName, String unitName) {
|
||||||
if(0 == amount){
|
if (0 == amount) {
|
||||||
return "零";
|
return "零";
|
||||||
}
|
}
|
||||||
Assert.checkBetween(amount, -99_9999_9999_9999.99, 99_9999_9999_9999.99,
|
Assert.checkBetween(amount, -99_9999_9999_9999.99, 99_9999_9999_9999.99,
|
||||||
@ -71,7 +81,7 @@ public class NumberChineseFormatter {
|
|||||||
|
|
||||||
// 负数
|
// 负数
|
||||||
if (amount < 0) {
|
if (amount < 0) {
|
||||||
chineseStr.append("负");
|
chineseStr.append(StrUtil.isNullOrUndefined(negativeName) ? "负" : negativeName);
|
||||||
amount = -amount;
|
amount = -amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,44 +92,44 @@ public class NumberChineseFormatter {
|
|||||||
yuan = yuan / 10;
|
yuan = yuan / 10;
|
||||||
|
|
||||||
// 元
|
// 元
|
||||||
if(false == isMoneyMode || 0 != yuan){
|
if (false == isMoneyMode || 0 != yuan) {
|
||||||
// 金额模式下,无需“零元”
|
// 金额模式下,无需“零元”
|
||||||
chineseStr.append(longToChinese(yuan, isUseTraditional));
|
chineseStr.append(longToChinese(yuan, isUseTraditional));
|
||||||
if(isMoneyMode){
|
if (isMoneyMode) {
|
||||||
chineseStr.append("元");
|
chineseStr.append(StrUtil.isNullOrUndefined(unitName) ? "元" : unitName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(0 == jiao && 0 == fen){
|
if (0 == jiao && 0 == fen) {
|
||||||
//无小数部分的金额结尾
|
//无小数部分的金额结尾
|
||||||
if(isMoneyMode){
|
if (isMoneyMode) {
|
||||||
chineseStr.append("整");
|
chineseStr.append("整");
|
||||||
}
|
}
|
||||||
return chineseStr.toString();
|
return chineseStr.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 小数部分
|
// 小数部分
|
||||||
if(false == isMoneyMode){
|
if (false == isMoneyMode) {
|
||||||
chineseStr.append("点");
|
chineseStr.append("点");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角
|
// 角
|
||||||
if(0 == yuan && 0 == jiao){
|
if (0 == yuan && 0 == jiao) {
|
||||||
// 元和角都为0时,只有非金额模式下补“零”
|
// 元和角都为0时,只有非金额模式下补“零”
|
||||||
if(false == isMoneyMode){
|
if (false == isMoneyMode) {
|
||||||
chineseStr.append("零");
|
chineseStr.append("零");
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
chineseStr.append(numberToChinese(jiao, isUseTraditional));
|
chineseStr.append(numberToChinese(jiao, isUseTraditional));
|
||||||
if(isMoneyMode && 0 != jiao){
|
if (isMoneyMode && 0 != jiao) {
|
||||||
chineseStr.append("角");
|
chineseStr.append("角");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分
|
// 分
|
||||||
if(0 != fen){
|
if (0 != fen) {
|
||||||
chineseStr.append(numberToChinese(fen, isUseTraditional));
|
chineseStr.append(numberToChinese(fen, isUseTraditional));
|
||||||
if(isMoneyMode){
|
if (isMoneyMode) {
|
||||||
chineseStr.append("分");
|
chineseStr.append("分");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,16 +137,28 @@ public class NumberChineseFormatter {
|
|||||||
return chineseStr.toString();
|
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 是否使用繁体
|
* @param isUseTraditional 是否使用繁体
|
||||||
* @return 中文
|
* @return 中文
|
||||||
* @since 5.7.17
|
* @since 5.7.17
|
||||||
*/
|
*/
|
||||||
public static String format(long amount, boolean isUseTraditional){
|
public static String format(long amount, boolean isUseTraditional) {
|
||||||
if(0 == amount){
|
if (0 == amount) {
|
||||||
return "零";
|
return "零";
|
||||||
}
|
}
|
||||||
Assert.checkBetween(amount, -99_9999_9999_9999.99, 99_9999_9999_9999.99,
|
Assert.checkBetween(amount, -99_9999_9999_9999.99, 99_9999_9999_9999.99,
|
||||||
@ -179,16 +201,16 @@ public class NumberChineseFormatter {
|
|||||||
* 格式化-999~999之间的数字<br>
|
* 格式化-999~999之间的数字<br>
|
||||||
* 这个方法显示10~19以下的数字时使用"十一"而非"一十一"。
|
* 这个方法显示10~19以下的数字时使用"十一"而非"一十一"。
|
||||||
*
|
*
|
||||||
* @param amount 数字
|
* @param amount 数字
|
||||||
* @param isUseTraditional 是否使用繁体
|
* @param isUseTraditional 是否使用繁体
|
||||||
* @return 中文
|
* @return 中文
|
||||||
* @since 5.7.17
|
* @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)!");
|
Assert.checkBetween(amount, -999, 999, "Number support only: (-999 ~ 999)!");
|
||||||
|
|
||||||
final String chinese = thousandToChinese(amount, isUseTraditional);
|
final String chinese = thousandToChinese(amount, isUseTraditional);
|
||||||
if(amount < 20 && amount >= 10){
|
if (amount < 20 && amount >= 10) {
|
||||||
// "十一"而非"一十一"
|
// "十一"而非"一十一"
|
||||||
return chinese.substring(1);
|
return chinese.substring(1);
|
||||||
}
|
}
|
||||||
@ -218,7 +240,7 @@ public class NumberChineseFormatter {
|
|||||||
* @return 中文
|
* @return 中文
|
||||||
*/
|
*/
|
||||||
private static String longToChinese(long amount, boolean isUseTraditional) {
|
private static String longToChinese(long amount, boolean isUseTraditional) {
|
||||||
if(0 == amount){
|
if (0 == amount) {
|
||||||
return "零";
|
return "零";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,11 +257,11 @@ public class NumberChineseFormatter {
|
|||||||
|
|
||||||
// 千
|
// 千
|
||||||
partValue = parts[0];
|
partValue = parts[0];
|
||||||
if(partValue > 0){
|
if (partValue > 0) {
|
||||||
partChinese = thousandToChinese(partValue, isUseTraditional);
|
partChinese = thousandToChinese(partValue, isUseTraditional);
|
||||||
chineseStr.insert(0, partChinese);
|
chineseStr.insert(0, partChinese);
|
||||||
|
|
||||||
if(partValue < 1000){
|
if (partValue < 1000) {
|
||||||
// 和万位之间空0,则补零,如一万零三百
|
// 和万位之间空0,则补零,如一万零三百
|
||||||
addPreZero(chineseStr);
|
addPreZero(chineseStr);
|
||||||
}
|
}
|
||||||
@ -247,26 +269,26 @@ public class NumberChineseFormatter {
|
|||||||
|
|
||||||
// 万
|
// 万
|
||||||
partValue = parts[1];
|
partValue = parts[1];
|
||||||
if(partValue > 0){
|
if (partValue > 0) {
|
||||||
if((partValue % 10 == 0 && parts[0] > 0)){
|
if ((partValue % 10 == 0 && parts[0] > 0)) {
|
||||||
// 如果"万"的个位是0,则补零,如十万零八千
|
// 如果"万"的个位是0,则补零,如十万零八千
|
||||||
addPreZero(chineseStr);
|
addPreZero(chineseStr);
|
||||||
}
|
}
|
||||||
partChinese = thousandToChinese(partValue, isUseTraditional);
|
partChinese = thousandToChinese(partValue, isUseTraditional);
|
||||||
chineseStr.insert(0, partChinese + "万");
|
chineseStr.insert(0, partChinese + "万");
|
||||||
|
|
||||||
if(partValue < 1000){
|
if (partValue < 1000) {
|
||||||
// 和亿位之间空0,则补零,如一亿零三百万
|
// 和亿位之间空0,则补零,如一亿零三百万
|
||||||
addPreZero(chineseStr);
|
addPreZero(chineseStr);
|
||||||
}
|
}
|
||||||
} else{
|
} else {
|
||||||
addPreZero(chineseStr);
|
addPreZero(chineseStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 亿
|
// 亿
|
||||||
partValue = parts[2];
|
partValue = parts[2];
|
||||||
if(partValue > 0){
|
if (partValue > 0) {
|
||||||
if((partValue % 10 == 0 && parts[1] > 0)){
|
if ((partValue % 10 == 0 && parts[1] > 0)) {
|
||||||
// 如果"万"的个位是0,则补零,如十万零八千
|
// 如果"万"的个位是0,则补零,如十万零八千
|
||||||
addPreZero(chineseStr);
|
addPreZero(chineseStr);
|
||||||
}
|
}
|
||||||
@ -274,25 +296,25 @@ public class NumberChineseFormatter {
|
|||||||
partChinese = thousandToChinese(partValue, isUseTraditional);
|
partChinese = thousandToChinese(partValue, isUseTraditional);
|
||||||
chineseStr.insert(0, partChinese + "亿");
|
chineseStr.insert(0, partChinese + "亿");
|
||||||
|
|
||||||
if(partValue < 1000){
|
if (partValue < 1000) {
|
||||||
// 和万亿位之间空0,则补零,如一万亿零三百亿
|
// 和万亿位之间空0,则补零,如一万亿零三百亿
|
||||||
addPreZero(chineseStr);
|
addPreZero(chineseStr);
|
||||||
}
|
}
|
||||||
} else{
|
} else {
|
||||||
addPreZero(chineseStr);
|
addPreZero(chineseStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 万亿
|
// 万亿
|
||||||
partValue = parts[3];
|
partValue = parts[3];
|
||||||
if(partValue > 0){
|
if (partValue > 0) {
|
||||||
if(parts[2] == 0){
|
if (parts[2] == 0) {
|
||||||
chineseStr.insert(0, "亿");
|
chineseStr.insert(0, "亿");
|
||||||
}
|
}
|
||||||
partChinese = thousandToChinese(partValue, isUseTraditional);
|
partChinese = thousandToChinese(partValue, isUseTraditional);
|
||||||
chineseStr.insert(0, partChinese + "万");
|
chineseStr.insert(0, partChinese + "万");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(StrUtil.isNotEmpty(chineseStr) && '零' == chineseStr.charAt(0)){
|
if (StrUtil.isNotEmpty(chineseStr) && '零' == chineseStr.charAt(0)) {
|
||||||
return chineseStr.substring(1);
|
return chineseStr.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +408,7 @@ public class NumberChineseFormatter {
|
|||||||
} else {
|
} else {
|
||||||
// 非节单位,和单位前的单数字组合为值
|
// 非节单位,和单位前的单数字组合为值
|
||||||
int unitNumber = number;
|
int unitNumber = number;
|
||||||
if(0 == number && 0 == i){
|
if (0 == number && 0 == i) {
|
||||||
// issue#1726,对于单位开头的数组,默认赋予1
|
// issue#1726,对于单位开头的数组,默认赋予1
|
||||||
// 十二 -> 一十二
|
// 十二 -> 一十二
|
||||||
// 百二 -> 一百二
|
// 百二 -> 一百二
|
||||||
@ -502,12 +524,12 @@ public class NumberChineseFormatter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addPreZero(StringBuilder chineseStr){
|
private static void addPreZero(StringBuilder chineseStr) {
|
||||||
if(StrUtil.isEmpty(chineseStr)){
|
if (StrUtil.isEmpty(chineseStr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final char c = chineseStr.charAt(0);
|
final char c = chineseStr.charAt(0);
|
||||||
if('零' != c){
|
if ('零' != c) {
|
||||||
chineseStr.insert(0, '零');
|
chineseStr.insert(0, '零');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,7 +700,7 @@ public class FileUtil extends PathUtil {
|
|||||||
if (null == file) {
|
if (null == file) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return mkdir(file.getParentFile());
|
return mkdir(getParent(file, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -267,8 +267,7 @@ public class FileCopier extends SrcToDestCopier<File, FileCopier>{
|
|||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
//路径不存在则创建父目录
|
//路径不存在则创建父目录
|
||||||
//noinspection ResultOfMethodCallIgnored
|
FileUtil.mkParentDirs(dest);
|
||||||
dest.getParentFile().mkdirs();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final ArrayList<CopyOption> optionList = new ArrayList<>(2);
|
final ArrayList<CopyOption> optionList = new ArrayList<>(2);
|
||||||
|
@ -97,7 +97,7 @@ public class NamingCase {
|
|||||||
// 后一个为大写,按照专有名词对待,如aBC -> a_BC
|
// 后一个为大写,按照专有名词对待,如aBC -> a_BC
|
||||||
} else {
|
} else {
|
||||||
//前一个为大写
|
//前一个为大写
|
||||||
if (null == nextChar || Character.isLowerCase(nextChar)) {
|
if (null != nextChar && Character.isLowerCase(nextChar)) {
|
||||||
// 普通首字母大写,如ABcc -> A_bcc
|
// 普通首字母大写,如ABcc -> A_bcc
|
||||||
sb.append(symbol);
|
sb.append(symbol);
|
||||||
c = Character.toLowerCase(c);
|
c = Character.toLowerCase(c);
|
||||||
@ -157,7 +157,7 @@ public class NamingCase {
|
|||||||
* 将连接符方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
|
* 将连接符方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
|
||||||
*
|
*
|
||||||
* @param name 转换前的自定义方式命名的字符串
|
* @param name 转换前的自定义方式命名的字符串
|
||||||
* @param symbol 连接符
|
* @param symbol 原字符串中的连接符连接符
|
||||||
* @return 转换后的驼峰式命名的字符串
|
* @return 转换后的驼峰式命名的字符串
|
||||||
* @since 5.7.17
|
* @since 5.7.17
|
||||||
*/
|
*/
|
||||||
|
@ -177,7 +177,7 @@ public class CsvBaseReader implements Serializable {
|
|||||||
final CsvParser csvParser = parse(reader);
|
final CsvParser csvParser = parse(reader);
|
||||||
final List<CsvRow> rows = new ArrayList<>();
|
final List<CsvRow> rows = new ArrayList<>();
|
||||||
read(csvParser, rows::add);
|
read(csvParser, rows::add);
|
||||||
final List<String> header = config.containsHeader ? csvParser.getHeader() : null;
|
final List<String> header = config.headerLineNo > -1 ? csvParser.getHeader() : null;
|
||||||
|
|
||||||
return new CsvData(header, rows);
|
return new CsvData(header, rows);
|
||||||
}
|
}
|
||||||
|
@ -84,13 +84,13 @@ public final class CsvParser extends ComputeIter<CsvRow> implements Closeable, S
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取头部字段列表,如果containsHeader设置为false则抛出异常
|
* 获取头部字段列表,如果headerLineNo < 0,抛出异常
|
||||||
*
|
*
|
||||||
* @return 头部列表
|
* @return 头部列表
|
||||||
* @throws IllegalStateException 如果不解析头部或者没有调用nextRow()方法
|
* @throws IllegalStateException 如果不解析头部或者没有调用nextRow()方法
|
||||||
*/
|
*/
|
||||||
public List<String> getHeader() {
|
public List<String> getHeader() {
|
||||||
if (false == config.containsHeader) {
|
if (config.headerLineNo < 0) {
|
||||||
throw new IllegalStateException("No header available - header parsing is disabled");
|
throw new IllegalStateException("No header available - header parsing is disabled");
|
||||||
}
|
}
|
||||||
if (lineNo < config.beginLineNo) {
|
if (lineNo < config.beginLineNo) {
|
||||||
@ -152,7 +152,7 @@ public final class CsvParser extends ComputeIter<CsvRow> implements Closeable, S
|
|||||||
}
|
}
|
||||||
|
|
||||||
//初始化标题
|
//初始化标题
|
||||||
if (config.containsHeader && null == header) {
|
if (lineNo == config.headerLineNo && null == header) {
|
||||||
initHeader(currentFields);
|
initHeader(currentFields);
|
||||||
// 作为标题行后,此行跳过,下一行做为第一行
|
// 作为标题行后,此行跳过,下一行做为第一行
|
||||||
continue;
|
continue;
|
||||||
|
@ -11,8 +11,8 @@ import java.io.Serializable;
|
|||||||
public class CsvReadConfig extends CsvConfig<CsvReadConfig> implements Serializable {
|
public class CsvReadConfig extends CsvConfig<CsvReadConfig> implements Serializable {
|
||||||
private static final long serialVersionUID = 5396453565371560052L;
|
private static final long serialVersionUID = 5396453565371560052L;
|
||||||
|
|
||||||
/** 是否首行做为标题行,默认false */
|
/** 指定标题行号,-1表示无标题行 */
|
||||||
protected boolean containsHeader;
|
protected long headerLineNo = -1;
|
||||||
/** 是否跳过空白行,默认true */
|
/** 是否跳过空白行,默认true */
|
||||||
protected boolean skipEmptyRows = true;
|
protected boolean skipEmptyRows = true;
|
||||||
/** 每行字段个数不同时是否抛出异常,默认false */
|
/** 每行字段个数不同时是否抛出异常,默认false */
|
||||||
@ -34,13 +34,26 @@ public class CsvReadConfig extends CsvConfig<CsvReadConfig> implements Serializa
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置是否首行做为标题行,默认false
|
* 设置是否首行做为标题行,默认false<br>
|
||||||
|
* 当设置为{@code true}时,默认标题行号是{@link #beginLineNo},{@code false}为-1,表示无行号
|
||||||
*
|
*
|
||||||
* @param containsHeader 是否首行做为标题行,默认false
|
* @param containsHeader 是否首行做为标题行,默认false
|
||||||
* @return this
|
* @return this
|
||||||
|
* @see #setHeaderLineNo(long)
|
||||||
*/
|
*/
|
||||||
public CsvReadConfig setContainsHeader(boolean containsHeader) {
|
public CsvReadConfig setContainsHeader(boolean containsHeader) {
|
||||||
this.containsHeader = containsHeader;
|
return setHeaderLineNo(containsHeader ? beginLineNo : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置标题行行号,默认-1,表示无标题行<br>
|
||||||
|
*
|
||||||
|
* @param headerLineNo 标题行行号,-1表示无标题行
|
||||||
|
* @return this
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public CsvReadConfig setHeaderLineNo(long headerLineNo) {
|
||||||
|
this.headerLineNo = headerLineNo;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,6 +368,48 @@ public class ArrayUtil extends PrimitiveArrayUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将新元素插入到到已有数组中的某个位置<br>
|
||||||
|
* 添加新元素会生成一个新数组或原有数组<br>
|
||||||
|
* 如果插入位置为为负数,那么生成一个由插入元素顺序加已有数组顺序的新数组
|
||||||
|
*
|
||||||
|
* @param <T> 数组元素类型
|
||||||
|
* @param buffer 已有数组
|
||||||
|
* @param index 位置,大于长度追加,否则替换,<0表示从头部追加
|
||||||
|
* @param values 新值
|
||||||
|
* @return 新数组或原有数组
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"unchecked"})
|
||||||
|
public static <T> 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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将新元素插入到到已有数组中的某个位置<br>
|
* 将新元素插入到到已有数组中的某个位置<br>
|
||||||
* 添加新元素会生成一个新的数组,不影响原数组<br>
|
* 添加新元素会生成一个新的数组,不影响原数组<br>
|
||||||
@ -1540,7 +1582,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
|
|||||||
* @since 4.5.18
|
* @since 4.5.18
|
||||||
*/
|
*/
|
||||||
public static boolean isAllEmpty(Object... args) {
|
public static boolean isAllEmpty(Object... args) {
|
||||||
for (Object obj: args) {
|
for (Object obj : args) {
|
||||||
if (false == ObjectUtil.isEmpty(obj)) {
|
if (false == ObjectUtil.isEmpty(obj)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1758,10 +1800,10 @@ public class ArrayUtil extends PrimitiveArrayUtil {
|
|||||||
/**
|
/**
|
||||||
* 查找最后一个子数组的开始位置
|
* 查找最后一个子数组的开始位置
|
||||||
*
|
*
|
||||||
* @param array 数组
|
* @param array 数组
|
||||||
* @param endInclude 查找结束的位置(包含)
|
* @param endInclude 查找结束的位置(包含)
|
||||||
* @param subArray 子数组
|
* @param subArray 子数组
|
||||||
* @param <T> 数组元素类型
|
* @param <T> 数组元素类型
|
||||||
* @return 最后一个子数组的开始位置,即子数字第一个元素在数组中的位置
|
* @return 最后一个子数组的开始位置,即子数字第一个元素在数组中的位置
|
||||||
* @since 5.4.8
|
* @since 5.4.8
|
||||||
*/
|
*/
|
||||||
|
@ -1,45 +1,59 @@
|
|||||||
package cn.hutool.core.io;
|
package cn.hutool.core.io;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import cn.hutool.core.io.file.FileCopier;
|
import cn.hutool.core.io.file.FileCopier;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件拷贝单元测试
|
* 文件拷贝单元测试
|
||||||
* @author Looly
|
|
||||||
*
|
*
|
||||||
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
public class FileCopierTest {
|
public class FileCopierTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void dirCopyTest() {
|
public void dirCopyTest() {
|
||||||
FileCopier copier = FileCopier.create("D:\\Java", "e:/eclipse/eclipse2.zip");
|
FileCopier copier = FileCopier.create("D:\\Java", "e:/eclipse/eclipse2.zip");
|
||||||
copier.copy();
|
copier.copy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void dirCopyTest2() {
|
public void dirCopyTest2() {
|
||||||
//测试带.的文件夹复制
|
//测试带.的文件夹复制
|
||||||
FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp");
|
FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp");
|
||||||
copier.copy();
|
copier.copy();
|
||||||
|
|
||||||
FileUtil.copy("D:\\workspace\\java\\looly\\hutool\\.git", "D:\\workspace\\java\\temp", true);
|
FileUtil.copy("D:\\workspace\\java\\looly\\hutool\\.git", "D:\\workspace\\java\\temp", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IORuntimeException.class)
|
@Test(expected = IORuntimeException.class)
|
||||||
public void dirCopySubTest() {
|
public void dirCopySubTest() {
|
||||||
//测试父目录复制到子目录报错
|
//测试父目录复制到子目录报错
|
||||||
FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp");
|
FileCopier copier = FileCopier.create("D:\\workspace\\java\\.metadata", "D:\\workspace\\java\\.metadata\\temp");
|
||||||
copier.copy();
|
copier.copy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void copyFileToDirTest() {
|
public void copyFileToDirTest() {
|
||||||
FileCopier copier = FileCopier.create("d:/GReen_Soft/XshellXftpPortable.zip", "c:/hp/");
|
FileCopier copier = FileCopier.create("d:/GReen_Soft/XshellXftpPortable.zip", "c:/hp/");
|
||||||
copier.copy();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,44 @@
|
|||||||
package cn.hutool.core.text;
|
package cn.hutool.core.text;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Dict;
|
import cn.hutool.core.lang.Dict;
|
||||||
|
import cn.hutool.core.util.CharUtil;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class NamingCaseTest {
|
public class NamingCaseTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void toUnderlineCaseTest(){
|
public void toCamelCaseTest() {
|
||||||
// https://github.com/dromara/hutool/issues/2070
|
|
||||||
Dict.create()
|
Dict.create()
|
||||||
.set("customerNickV2", "customer_nick_v2")
|
.set("Table_Test_Of_day","tableTestOfDay")
|
||||||
.forEach((key, value) -> Assert.assertEquals(value, NamingCase.toUnderlineCase(key)));
|
.set("TableTestOfDay","TableTestOfDay")
|
||||||
|
.set("abc_1d","abc1d")
|
||||||
|
.forEach((key, value) -> Assert.assertEquals(value, NamingCase.toCamelCase(key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void toUnderLineCaseTest2(){
|
public void toCamelCaseFromDashedTest() {
|
||||||
final String wPRunOZTime = NamingCase.toUnderlineCase("wPRunOZTime");
|
Dict.create()
|
||||||
Assert.assertEquals("w_P_run_OZ_time", wPRunOZTime);
|
.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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -363,7 +363,7 @@ public class ArrayUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void indexOfSubTest2(){
|
public void indexOfSubTest2() {
|
||||||
Integer[] a = {0x12, 0x56, 0x34, 0x56, 0x78, 0x9A};
|
Integer[] a = {0x12, 0x56, 0x34, 0x56, 0x78, 0x9A};
|
||||||
Integer[] b = {0x56, 0x78};
|
Integer[] b = {0x56, 0x78};
|
||||||
int i = ArrayUtil.indexOfSub(a, b);
|
int i = ArrayUtil.indexOfSub(a, b);
|
||||||
@ -401,7 +401,7 @@ public class ArrayUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void lastIndexOfSubTest2(){
|
public void lastIndexOfSubTest2() {
|
||||||
Integer[] a = {0x12, 0x56, 0x78, 0x56, 0x21, 0x9A};
|
Integer[] a = {0x12, 0x56, 0x78, 0x56, 0x21, 0x9A};
|
||||||
Integer[] b = {0x56, 0x78};
|
Integer[] b = {0x56, 0x78};
|
||||||
int i = ArrayUtil.indexOfSub(a, b);
|
int i = ArrayUtil.indexOfSub(a, b);
|
||||||
@ -409,17 +409,17 @@ public class ArrayUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void reverseTest(){
|
public void reverseTest() {
|
||||||
int[] a = {1,2,3,4};
|
int[] a = {1, 2, 3, 4};
|
||||||
final int[] reverse = ArrayUtil.reverse(a);
|
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
|
@Test
|
||||||
public void reverseTest2s(){
|
public void reverseTest2s() {
|
||||||
Object[] a = {"1",'2',"3",4};
|
Object[] a = {"1", '2', "3", 4};
|
||||||
final Object[] reverse = ArrayUtil.reverse(a);
|
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
|
@Test
|
||||||
@ -461,9 +461,57 @@ public class ArrayUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getTest(){
|
public void getTest() {
|
||||||
String[] a = {"a", "b", "c"};
|
String[] a = {"a", "b", "c"};
|
||||||
final Object o = ArrayUtil.get(a, -1);
|
final Object o = ArrayUtil.get(a, -1);
|
||||||
Assert.assertEquals("c", o);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -391,40 +391,6 @@ public class StrUtilTest {
|
|||||||
Assert.assertEquals(text, str);
|
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
|
@Test
|
||||||
public void containsAnyTest() {
|
public void containsAnyTest() {
|
||||||
//字符
|
//字符
|
||||||
|
@ -20,10 +20,9 @@
|
|||||||
<!-- versions -->
|
<!-- versions -->
|
||||||
<c3p0.version>0.9.5.5</c3p0.version>
|
<c3p0.version>0.9.5.5</c3p0.version>
|
||||||
<dbcp2.version>2.9.0</dbcp2.version>
|
<dbcp2.version>2.9.0</dbcp2.version>
|
||||||
<tomcat-jdbc.version>10.0.14</tomcat-jdbc.version>
|
<tomcat-jdbc.version>10.0.16</tomcat-jdbc.version>
|
||||||
<druid.version>1.2.8</druid.version>
|
<druid.version>1.2.8</druid.version>
|
||||||
<hikariCP.version>2.4.13</hikariCP.version>
|
<hikariCP.version>2.4.13</hikariCP.version>
|
||||||
<mongo.version>3.12.10</mongo.version>
|
|
||||||
<mongo4.version>4.5.0</mongo4.version>
|
<mongo4.version>4.5.0</mongo4.version>
|
||||||
<sqlite.version>3.36.0.3</sqlite.version>
|
<sqlite.version>3.36.0.3</sqlite.version>
|
||||||
<!-- 此处固定2.5.x,支持到JDK8 -->
|
<!-- 此处固定2.5.x,支持到JDK8 -->
|
||||||
@ -97,13 +96,6 @@
|
|||||||
<version>${dbcp2.version}</version>
|
<version>${dbcp2.version}</version>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- MongoDB Java客户端 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mongodb</groupId>
|
|
||||||
<artifactId>mongo-java-driver</artifactId>
|
|
||||||
<version>${mongo.version}</version>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mongodb</groupId>
|
<groupId>org.mongodb</groupId>
|
||||||
<artifactId>mongodb-driver-sync</artifactId>
|
<artifactId>mongodb-driver-sync</artifactId>
|
||||||
|
@ -6,29 +6,35 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import cn.hutool.db.DbRuntimeException;
|
import cn.hutool.db.DbRuntimeException;
|
||||||
import cn.hutool.log.Log;
|
import cn.hutool.log.Log;
|
||||||
import cn.hutool.setting.Setting;
|
import cn.hutool.setting.Setting;
|
||||||
import com.mongodb.MongoClient;
|
import com.mongodb.MongoClientSettings;
|
||||||
import com.mongodb.MongoClientOptions;
|
|
||||||
import com.mongodb.MongoClientOptions.Builder;
|
|
||||||
import com.mongodb.MongoCredential;
|
import com.mongodb.MongoCredential;
|
||||||
import com.mongodb.ServerAddress;
|
import com.mongodb.ServerAddress;
|
||||||
|
import com.mongodb.client.MongoClient;
|
||||||
|
import com.mongodb.client.MongoClients;
|
||||||
import com.mongodb.client.MongoCollection;
|
import com.mongodb.client.MongoCollection;
|
||||||
import com.mongodb.client.MongoDatabase;
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import com.mongodb.connection.ConnectionPoolSettings;
|
||||||
|
import com.mongodb.connection.SocketSettings;
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MongoDB工具类
|
* MongoDB4工具类
|
||||||
*
|
|
||||||
* @author xiaoleilu
|
|
||||||
*
|
*
|
||||||
|
* @author VampireAchao
|
||||||
*/
|
*/
|
||||||
public class MongoDS implements Closeable {
|
public class MongoDS implements Closeable {
|
||||||
|
|
||||||
private final static Log log = Log.get();
|
private final static Log log = Log.get();
|
||||||
|
|
||||||
/** 默认配置文件 */
|
/**
|
||||||
|
* 默认配置文件
|
||||||
|
*/
|
||||||
public final static String MONGO_CONFIG_PATH = "config/mongo.setting";
|
public final static String MONGO_CONFIG_PATH = "config/mongo.setting";
|
||||||
|
|
||||||
// MongoDB配置文件
|
// MongoDB配置文件
|
||||||
@ -41,6 +47,7 @@ public class MongoDS implements Closeable {
|
|||||||
private MongoClient mongo;
|
private MongoClient mongo;
|
||||||
|
|
||||||
// --------------------------------------------------------------------------- Constructor start
|
// --------------------------------------------------------------------------- Constructor start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造MongoDB数据源<br>
|
* 构造MongoDB数据源<br>
|
||||||
* 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败!
|
* 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败!
|
||||||
@ -58,8 +65,8 @@ public class MongoDS implements Closeable {
|
|||||||
* 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败!
|
* 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败!
|
||||||
*
|
*
|
||||||
* @param mongoSetting MongoDB的配置文件,如果是null则读取默认配置文件或者使用MongoDB默认客户端配置
|
* @param mongoSetting MongoDB的配置文件,如果是null则读取默认配置文件或者使用MongoDB默认客户端配置
|
||||||
* @param host 主机(域名或者IP)
|
* @param host 主机(域名或者IP)
|
||||||
* @param port 端口
|
* @param port 端口
|
||||||
*/
|
*/
|
||||||
public MongoDS(Setting mongoSetting, String host, int port) {
|
public MongoDS(Setting mongoSetting, String host, int port) {
|
||||||
this.setting = mongoSetting;
|
this.setting = mongoSetting;
|
||||||
@ -86,7 +93,7 @@ public class MongoDS implements Closeable {
|
|||||||
* 官方文档: http://docs.mongodb.org/manual/administration/replica-sets/
|
* 官方文档: http://docs.mongodb.org/manual/administration/replica-sets/
|
||||||
*
|
*
|
||||||
* @param mongoSetting MongoDB的配置文件,必须有
|
* @param mongoSetting MongoDB的配置文件,必须有
|
||||||
* @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式
|
* @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式
|
||||||
*/
|
*/
|
||||||
public MongoDS(Setting mongoSetting, String... groups) {
|
public MongoDS(Setting mongoSetting, String... groups) {
|
||||||
if (mongoSetting == null) {
|
if (mongoSetting == null) {
|
||||||
@ -146,11 +153,13 @@ public class MongoDS implements Closeable {
|
|||||||
|
|
||||||
final MongoCredential credentail = createCredentail(group);
|
final MongoCredential credentail = createCredentail(group);
|
||||||
try {
|
try {
|
||||||
if (null == credentail) {
|
MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder()
|
||||||
mongo = new MongoClient(serverAddress, buildMongoClientOptions(group));
|
.applyToClusterSettings(b -> b.hosts(Collections.singletonList(serverAddress)));
|
||||||
} else {
|
buildMongoClientSettings(clusterSettingsBuilder, group);
|
||||||
mongo = new MongoClient(serverAddress, credentail, buildMongoClientOptions(group));
|
if (null != credentail) {
|
||||||
|
clusterSettingsBuilder.credential(credentail);
|
||||||
}
|
}
|
||||||
|
mongo = MongoClients.create(clusterSettingsBuilder.build());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new DbRuntimeException(StrUtil.format("Init MongoDB pool with connection to [{}] error!", serverAddress), 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);
|
final MongoCredential credentail = createCredentail(StrUtil.EMPTY);
|
||||||
try {
|
try {
|
||||||
if (null == credentail) {
|
MongoClientSettings.Builder clusterSettingsBuilder = MongoClientSettings.builder()
|
||||||
mongo = new MongoClient(addrList, buildMongoClientOptions(StrUtil.EMPTY));
|
.applyToClusterSettings(b -> b.hosts(addrList));
|
||||||
} else {
|
buildMongoClientSettings(clusterSettingsBuilder, StrUtil.EMPTY);
|
||||||
mongo = new MongoClient(addrList, credentail, buildMongoClientOptions(StrUtil.EMPTY));
|
if (null != credentail) {
|
||||||
|
clusterSettingsBuilder.credential(credentail);
|
||||||
}
|
}
|
||||||
|
mongo = MongoClients.create(clusterSettingsBuilder.build());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e, "Init MongoDB connection error!");
|
log.error(e, "Init MongoDB connection error!");
|
||||||
return;
|
return;
|
||||||
@ -234,7 +245,7 @@ public class MongoDS implements Closeable {
|
|||||||
/**
|
/**
|
||||||
* 获得MongoDB中指定集合对象
|
* 获得MongoDB中指定集合对象
|
||||||
*
|
*
|
||||||
* @param dbName 库名
|
* @param dbName 库名
|
||||||
* @param collectionName 集合名
|
* @param collectionName 集合名
|
||||||
* @return DBCollection
|
* @return DBCollection
|
||||||
*/
|
*/
|
||||||
@ -248,6 +259,7 @@ public class MongoDS implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------- Private method start
|
// --------------------------------------------------------------------------- Private method start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建ServerAddress对象,会读取配置文件中的相关信息
|
* 创建ServerAddress对象,会读取配置文件中的相关信息
|
||||||
*
|
*
|
||||||
@ -291,7 +303,7 @@ public class MongoDS implements Closeable {
|
|||||||
*/
|
*/
|
||||||
private MongoCredential createCredentail(String group) {
|
private MongoCredential createCredentail(String group) {
|
||||||
final Setting setting = this.setting;
|
final Setting setting = this.setting;
|
||||||
if(null == setting) {
|
if (null == setting) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final String user = setting.getStr("user", group, setting.getStr("user"));
|
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());
|
return MongoCredential.createCredential(userName, database, password.toCharArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 构件MongoDB连接选项<br>
|
|
||||||
*
|
|
||||||
* @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值
|
|
||||||
* @return MongoClientOptions
|
|
||||||
*/
|
|
||||||
private MongoClientOptions buildMongoClientOptions(String group) {
|
|
||||||
return buildMongoClientOptions(MongoClientOptions.builder(), group).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构件MongoDB连接选项<br>
|
* 构件MongoDB连接选项<br>
|
||||||
*
|
*
|
||||||
* @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值
|
* @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值
|
||||||
* @return Builder
|
* @return Builder
|
||||||
*/
|
*/
|
||||||
private Builder buildMongoClientOptions(Builder builder, String group) {
|
private MongoClientSettings.Builder buildMongoClientSettings(MongoClientSettings.Builder builder, String group) {
|
||||||
if (setting == null) {
|
if (setting == null) {
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
@ -348,8 +350,9 @@ public class MongoDS implements Closeable {
|
|||||||
if (StrUtil.isBlank(group) == false && connectionsPerHost == null) {
|
if (StrUtil.isBlank(group) == false && connectionsPerHost == null) {
|
||||||
connectionsPerHost = setting.getInt("connectionsPerHost");
|
connectionsPerHost = setting.getInt("connectionsPerHost");
|
||||||
}
|
}
|
||||||
|
ConnectionPoolSettings.Builder connectionPoolSettingsBuilder = ConnectionPoolSettings.builder();
|
||||||
if (connectionsPerHost != null) {
|
if (connectionsPerHost != null) {
|
||||||
builder.connectionsPerHost(connectionsPerHost);
|
connectionPoolSettingsBuilder.maxSize(connectionsPerHost);
|
||||||
log.debug("MongoDB connectionsPerHost: {}", connectionsPerHost);
|
log.debug("MongoDB connectionsPerHost: {}", connectionsPerHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,9 +362,10 @@ public class MongoDS implements Closeable {
|
|||||||
setting.getInt("connectTimeout");
|
setting.getInt("connectTimeout");
|
||||||
}
|
}
|
||||||
if (connectTimeout != null) {
|
if (connectTimeout != null) {
|
||||||
builder.connectTimeout(connectTimeout);
|
connectionPoolSettingsBuilder.maxWaitTime(connectTimeout, TimeUnit.MILLISECONDS);
|
||||||
log.debug("MongoDB connectTimeout: {}", connectTimeout);
|
log.debug("MongoDB connectTimeout: {}", connectTimeout);
|
||||||
}
|
}
|
||||||
|
builder.applyToConnectionPoolSettings(b -> b.applySettings(connectionPoolSettingsBuilder.build()));
|
||||||
|
|
||||||
// 套接字超时时间;该值会被传递给Socket.setSoTimeout(int)。默以为0(无穷) --int
|
// 套接字超时时间;该值会被传递给Socket.setSoTimeout(int)。默以为0(无穷) --int
|
||||||
Integer socketTimeout = setting.getInt(group + "socketTimeout");
|
Integer socketTimeout = setting.getInt(group + "socketTimeout");
|
||||||
@ -369,7 +373,8 @@ public class MongoDS implements Closeable {
|
|||||||
setting.getInt("socketTimeout");
|
setting.getInt("socketTimeout");
|
||||||
}
|
}
|
||||||
if (socketTimeout != null) {
|
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);
|
log.debug("MongoDB socketTimeout: {}", socketTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,4 +393,5 @@ public class MongoDS implements Closeable {
|
|||||||
return this.setting;
|
return this.setting;
|
||||||
}
|
}
|
||||||
// --------------------------------------------------------------------------- Private method end
|
// --------------------------------------------------------------------------- Private method end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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数据源<br>
|
|
||||||
* 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败!
|
|
||||||
*
|
|
||||||
* @param host 主机(域名或者IP)
|
|
||||||
* @param port 端口
|
|
||||||
*/
|
|
||||||
public MongoDS4(String host, int port) {
|
|
||||||
this.serverAddress = createServerAddress(host, port);
|
|
||||||
initSingle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造MongoDB数据源<br>
|
|
||||||
* 调用者必须持有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数据源<br>
|
|
||||||
* 当提供多个数据源时,这些数据源将为一个副本集或者多个mongos<br>
|
|
||||||
* 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败! 官方文档: http://docs.mongodb.org/manual/administration/replica-sets/
|
|
||||||
*
|
|
||||||
* @param groups 分组列表,当为null或空时使用无分组配置,一个分组使用单一模式,否则使用副本集模式
|
|
||||||
*/
|
|
||||||
public MongoDS4(String... groups) {
|
|
||||||
this.groups = groups;
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造MongoDB数据源<br>
|
|
||||||
* 当提供多个数据源时,这些数据源将为一个副本集或者mongos<br>
|
|
||||||
* 调用者必须持有MongoDS实例,否则会被垃圾回收导致写入失败!<br>
|
|
||||||
* 官方文档: 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化<br>
|
|
||||||
* 设定文件中的host和端口有三种形式:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* host = host:port
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* host = host
|
|
||||||
* port = port
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* host = host
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化集群<br>
|
|
||||||
* 集群的其它客户端设定参数使用全局设定<br>
|
|
||||||
* 集群中每一个实例成员用一个group表示,例如:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* 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
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
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<ServerAddress> 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<Document> 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},用于服务端验证<br>
|
|
||||||
* 此方法会首先读取指定分组下的属性,用户没有定义则读取空分组下的属性
|
|
||||||
*
|
|
||||||
* @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连接选项<br>
|
|
||||||
*
|
|
||||||
* @param group 分组,当分组对应的选项不存在时会读取根选项,如果也不存在使用默认值
|
|
||||||
* @return MongoClientOptions
|
|
||||||
*/
|
|
||||||
private MongoClientSettings buildMongoClientOptions(String group) {
|
|
||||||
return buildMongoClientOptions(MongoClientSettings.builder(), group).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构件MongoDB连接选项<br>
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
|
|
||||||
}
|
|
@ -10,16 +10,20 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MongoDB工厂类,用于创建
|
* {@link MongoDS}工厂类,用于创建
|
||||||
* @author looly
|
|
||||||
*
|
*
|
||||||
|
* @author Looly, VampireAchao
|
||||||
*/
|
*/
|
||||||
public class MongoFactory {
|
public class MongoFactory {
|
||||||
|
|
||||||
/** 各分组做组合key的时候分隔符 */
|
/**
|
||||||
|
* 各分组做组合key的时候分隔符
|
||||||
|
*/
|
||||||
private final static String GROUP_SEPRATER = ",";
|
private final static String GROUP_SEPRATER = ",";
|
||||||
|
|
||||||
/** 数据源池 */
|
/**
|
||||||
|
* 数据源池
|
||||||
|
*/
|
||||||
private static final Map<String, MongoDS> DS_MAP = new ConcurrentHashMap<>();
|
private static final Map<String, MongoDS> DS_MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// JVM关闭前关闭MongoDB连接
|
// JVM关闭前关闭MongoDB连接
|
||||||
@ -28,6 +32,7 @@ public class MongoFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------ Get DS start
|
// ------------------------------------------------------------------------ Get DS start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取MongoDB数据源<br>
|
* 获取MongoDB数据源<br>
|
||||||
*
|
*
|
||||||
@ -80,7 +85,7 @@ public class MongoFactory {
|
|||||||
* 获取MongoDB数据源<br>
|
* 获取MongoDB数据源<br>
|
||||||
*
|
*
|
||||||
* @param setting 设定文件
|
* @param setting 设定文件
|
||||||
* @param groups 分组列表
|
* @param groups 分组列表
|
||||||
* @return MongoDB连接
|
* @return MongoDB连接
|
||||||
*/
|
*/
|
||||||
public static MongoDS getDS(Setting setting, String... groups) {
|
public static MongoDS getDS(Setting setting, String... groups) {
|
||||||
@ -99,7 +104,7 @@ public class MongoFactory {
|
|||||||
* 获取MongoDB数据源<br>
|
* 获取MongoDB数据源<br>
|
||||||
*
|
*
|
||||||
* @param setting 配置文件
|
* @param setting 配置文件
|
||||||
* @param groups 分组列表
|
* @param groups 分组列表
|
||||||
* @return MongoDB连接
|
* @return MongoDB连接
|
||||||
*/
|
*/
|
||||||
public static MongoDS getDS(Setting setting, Collection<String> groups) {
|
public static MongoDS getDS(Setting setting, Collection<String> groups) {
|
||||||
@ -111,8 +116,8 @@ public class MongoFactory {
|
|||||||
* 关闭全部连接
|
* 关闭全部连接
|
||||||
*/
|
*/
|
||||||
public static void closeAll() {
|
public static void closeAll() {
|
||||||
if(MapUtil.isNotEmpty(DS_MAP)){
|
if (MapUtil.isNotEmpty(DS_MAP)) {
|
||||||
for(MongoDS ds : DS_MAP.values()) {
|
for (MongoDS ds : DS_MAP.values()) {
|
||||||
ds.close();
|
ds.close();
|
||||||
}
|
}
|
||||||
DS_MAP.clear();
|
DS_MAP.clear();
|
||||||
|
@ -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<String, MongoDS4> DS_MAP = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
// JVM关闭前关闭MongoDB连接
|
|
||||||
static {
|
|
||||||
RuntimeUtil.addShutdownHook(MongoFactory4::closeAll);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------ Get DS start
|
|
||||||
/**
|
|
||||||
* 获取MongoDB数据源<br>
|
|
||||||
*
|
|
||||||
* @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数据源<br>
|
|
||||||
* 多个分组名对应的连接组成集群
|
|
||||||
*
|
|
||||||
* @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数据源<br>
|
|
||||||
*
|
|
||||||
* @param groups 分组列表
|
|
||||||
* @return MongoDB连接
|
|
||||||
*/
|
|
||||||
public static MongoDS4 getDS(Collection<String> groups) {
|
|
||||||
return getDS(groups.toArray(new String[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取MongoDB数据源<br>
|
|
||||||
*
|
|
||||||
* @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数据源<br>
|
|
||||||
*
|
|
||||||
* @param setting 配置文件
|
|
||||||
* @param groups 分组列表
|
|
||||||
* @return MongoDB连接
|
|
||||||
*/
|
|
||||||
public static MongoDS4 getDS(Setting setting, Collection<String> 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
package cn.hutool.db.nosql;
|
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 com.mongodb.client.MongoDatabase;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
@ -13,8 +13,8 @@ public class MongoDBTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void redisDSTest() {
|
public void mongoDSTest() {
|
||||||
MongoDatabase db = MongoFactory4.getDS("master").getDb("test");
|
MongoDatabase db = MongoFactory.getDS("master").getDb("test");
|
||||||
Assert.assertEquals("test", db.getName());
|
Assert.assertEquals("test", db.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@ socketKeepAlive=false
|
|||||||
|
|
||||||
#---------------------------------- MongoDB实例连接
|
#---------------------------------- MongoDB实例连接
|
||||||
[master]
|
[master]
|
||||||
host = 127.0.0.1:27017
|
host = localhost:27017
|
||||||
|
|
||||||
[slave]
|
[slave]
|
||||||
host = 127.0.0.1:27018
|
host = localhost:27018
|
||||||
#-----------------------------------------------------
|
#-----------------------------------------------------
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
# Auto Configure
|
# Auto Configure
|
||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
cn.hutool.extra.spring.SpringUtil
|
cn.hutool.extra.spring.SpringUtil
|
||||||
|
@ -1161,10 +1161,11 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 对于GET请求将参数加到URL中<br>
|
* 对于GET请求将参数加到URL中<br>
|
||||||
* 此处不对URL中的特殊字符做单独编码
|
* 此处不对URL中的特殊字符做单独编码<br>
|
||||||
|
* 对于非rest的GET请求,且处于重定向时,参数丢弃
|
||||||
*/
|
*/
|
||||||
private void urlWithParamIfGet() {
|
private void urlWithParamIfGet() {
|
||||||
if (Method.GET.equals(method) && false == this.isRest) {
|
if (Method.GET.equals(method) && false == this.isRest && this.redirectCount > 0) {
|
||||||
// 优先使用body形式的参数,不存在使用form
|
// 优先使用body形式的参数,不存在使用form
|
||||||
if (ArrayUtil.isNotEmpty(this.bodyBytes)) {
|
if (ArrayUtil.isNotEmpty(this.bodyBytes)) {
|
||||||
this.url.getQuery().parse(StrUtil.str(this.bodyBytes, this.charset), this.charset);
|
this.url.getQuery().parse(StrUtil.str(this.bodyBytes, this.charset), this.charset);
|
||||||
@ -1194,7 +1195,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
|||||||
|
|
||||||
if (responseCode != HttpURLConnection.HTTP_OK) {
|
if (responseCode != HttpURLConnection.HTTP_OK) {
|
||||||
if (HttpStatus.isRedirected(responseCode)) {
|
if (HttpStatus.isRedirected(responseCode)) {
|
||||||
setUrl(httpConnection.header(Header.LOCATION));
|
setUrl(UrlBuilder.ofHttpWithoutEncode(httpConnection.header(Header.LOCATION)));
|
||||||
if (redirectCount < this.maxRedirectCount) {
|
if (redirectCount < this.maxRedirectCount) {
|
||||||
redirectCount++;
|
redirectCount++;
|
||||||
// 重定向不再走过滤器
|
// 重定向不再走过滤器
|
||||||
|
@ -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<TestEntity> 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<TestEntity> 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user