mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-12-08 01:59:14 +08:00
add TomlConfigParser
This commit is contained in:
parent
1f5e574974
commit
8ae9643377
@ -23,7 +23,7 @@ import java.util.Iterator;
|
||||
* 参考:<a href="http://stackoverflow.com/a/21791059/6030888">http://stackoverflow.com/a/21791059/6030888</a>
|
||||
*
|
||||
* @author Looly
|
||||
* @param str
|
||||
* @param str 字符串
|
||||
*/
|
||||
public record CodePointIter(String str) implements Iterable<Integer> {
|
||||
|
||||
|
||||
@ -0,0 +1,237 @@
|
||||
package cn.hutool.v7.db.config;
|
||||
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import cn.hutool.v7.core.convert.ConvertUtil;
|
||||
import cn.hutool.v7.core.io.resource.NoResourceException;
|
||||
import cn.hutool.v7.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.core.util.ObjUtil;
|
||||
import cn.hutool.v7.db.DbException;
|
||||
import cn.hutool.v7.db.driver.DriverUtil;
|
||||
import cn.hutool.v7.db.sql.SqlLog;
|
||||
import cn.hutool.v7.db.sql.filter.SqlLogFilter;
|
||||
import cn.hutool.v7.log.level.Level;
|
||||
import cn.hutool.v7.setting.toml.TomlReader;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 基于TOML类型的数据库配置解析器
|
||||
*
|
||||
* @author Looly
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public class TomlConfigParser implements ConfigParser {
|
||||
|
||||
private static final String CONNECTION_PREFIX = "connection.";
|
||||
|
||||
/**
|
||||
* 默认TOML配置文件路径
|
||||
*/
|
||||
private static final String[] DEFAULT_DB_TOML_PATHS = {"config/db.toml", "db.toml"};
|
||||
|
||||
/**
|
||||
* 创建默认配置解析器
|
||||
*
|
||||
* @return TomlConfigParser
|
||||
*/
|
||||
public static TomlConfigParser of() {
|
||||
return of(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建配置解析器
|
||||
*
|
||||
* @param tomlPath TOML配置文件路径
|
||||
* @return TomlConfigParser
|
||||
*/
|
||||
public static TomlConfigParser of(final String tomlPath) {
|
||||
return new TomlConfigParser(tomlPath);
|
||||
}
|
||||
|
||||
private final Map<String, Object> tomlData;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param tomlPath 自定义TOML配置文件路径,{@code null}表示使用默认路径
|
||||
*/
|
||||
public TomlConfigParser(final String tomlPath) {
|
||||
String tomlContent = null;
|
||||
if (null != tomlPath) {
|
||||
// 读取指定TOML文件
|
||||
tomlContent = ResourceUtil.readUtf8Str(tomlPath);
|
||||
} else {
|
||||
// 读取默认TOML文件
|
||||
for (String defaultDbTomlPath : DEFAULT_DB_TOML_PATHS) {
|
||||
try {
|
||||
tomlContent = ResourceUtil.readUtf8Str(defaultDbTomlPath);
|
||||
} catch (NoResourceException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null == tomlContent) {
|
||||
throw new NoResourceException("Default db toml [{}] in classpath not found !", ArrayUtil.join(DEFAULT_DB_TOML_PATHS, ","));
|
||||
}
|
||||
|
||||
this.tomlData = new TomlReader(tomlContent, false).read();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public DbConfig parse(final String group) {
|
||||
Map<String, Object> groupData;
|
||||
|
||||
if (StrUtil.isEmpty(group)) {
|
||||
groupData = this.tomlData;
|
||||
} else {
|
||||
Object groupObj = this.tomlData.get(group);
|
||||
if (groupObj instanceof Map) {
|
||||
// 新建一个Map,避免继承属性影响原数据
|
||||
groupData = new HashMap<>((Map<String, Object>) groupObj);
|
||||
} else {
|
||||
throw new DbException("No config for group: [{}]", group);
|
||||
}
|
||||
}
|
||||
|
||||
// 继承属性
|
||||
copyPropertyIfAbsent(groupData, DSKeys.KEY_SHOW_SQL);
|
||||
copyPropertyIfAbsent(groupData, DSKeys.KEY_FORMAT_SQL);
|
||||
copyPropertyIfAbsent(groupData, DSKeys.KEY_SHOW_PARAMS);
|
||||
copyPropertyIfAbsent(groupData, DSKeys.KEY_SQL_LEVEL);
|
||||
|
||||
return toDbConfig(groupData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果目标map中没有指定键,则从全局配置复制
|
||||
*/
|
||||
private void copyPropertyIfAbsent(Map<String, Object> target, String key) {
|
||||
if (!target.containsKey(key) && this.tomlData.containsKey(key)) {
|
||||
target.put(key, this.tomlData.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将TOML数据转换为DbConfig对象
|
||||
*/
|
||||
private DbConfig toDbConfig(Map<String, Object> data) {
|
||||
// 基本信息
|
||||
String url = getAndRemoveString(data, DSKeys.KEY_ALIAS_URL);
|
||||
if (StrUtil.isBlank(url)) {
|
||||
throw new DbException("No JDBC URL!");
|
||||
}
|
||||
|
||||
// 自动识别Driver
|
||||
String driver = getAndRemoveString(data, DSKeys.KEY_ALIAS_DRIVER);
|
||||
if (StrUtil.isBlank(driver)) {
|
||||
driver = DriverUtil.identifyDriver(url);
|
||||
}
|
||||
|
||||
DbConfig dbConfig = DbConfig.of()
|
||||
.setUrl(url)
|
||||
.setDriver(driver)
|
||||
.setUser(getAndRemoveString(data, DSKeys.KEY_ALIAS_USER))
|
||||
.setPass(getAndRemoveString(data, DSKeys.KEY_ALIAS_PASSWORD));
|
||||
|
||||
// SQL日志
|
||||
SqlLogFilter sqlLogFilter = getSqlLogFilter(data);
|
||||
if (sqlLogFilter != null) {
|
||||
dbConfig.addSqlFilter(sqlLogFilter);
|
||||
}
|
||||
|
||||
// 大小写等配置
|
||||
Boolean caseInsensitive = getAndRemoveBoolean(data, DSKeys.KEY_CASE_INSENSITIVE);
|
||||
if (null != caseInsensitive) {
|
||||
dbConfig.setCaseInsensitive(caseInsensitive);
|
||||
}
|
||||
|
||||
// 连接配置
|
||||
for (String key : DSKeys.KEY_CONN_PROPS) {
|
||||
String connValue = getAndRemoveString(data, key);
|
||||
if (StrUtil.isNotBlank(connValue)) {
|
||||
dbConfig.addConnProps(key, connValue);
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义连接属性
|
||||
data.forEach((key, value) -> {
|
||||
if(key.startsWith(CONNECTION_PREFIX) && !(value instanceof Map)){
|
||||
dbConfig.addConnProps(key.substring(CONNECTION_PREFIX.length()), value.toString());
|
||||
}
|
||||
});
|
||||
|
||||
// 池属性 - 移除已处理的属性后剩余的作为池属性
|
||||
data.forEach((key, value) -> {
|
||||
// 非Map的属性作为池属性
|
||||
if(!(value instanceof Map)){
|
||||
dbConfig.addPoolProps(key, StrUtil.toStringOrNull(value));
|
||||
}
|
||||
});
|
||||
|
||||
return dbConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SQL日志过滤器
|
||||
*/
|
||||
private SqlLogFilter getSqlLogFilter(Map<String, Object> data) {
|
||||
final Boolean isShowSql = getAndRemoveBoolean(data, DSKeys.KEY_SHOW_SQL);
|
||||
if (isShowSql == null || !isShowSql) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final boolean isFormatSql = ObjUtil.defaultIfNull(getAndRemoveBoolean(data, DSKeys.KEY_FORMAT_SQL), false);
|
||||
final boolean isShowParams = ObjUtil.defaultIfNull(getAndRemoveBoolean(data, DSKeys.KEY_SHOW_PARAMS), false);
|
||||
|
||||
String sqlLevelStr = getAndRemoveString(data, DSKeys.KEY_SQL_LEVEL);
|
||||
if (sqlLevelStr != null) {
|
||||
sqlLevelStr = sqlLevelStr.toUpperCase();
|
||||
}
|
||||
Level level = ConvertUtil.toEnum(Level.class, sqlLevelStr, Level.DEBUG);
|
||||
|
||||
SqlLog sqlLog = new SqlLog();
|
||||
sqlLog.init(true, isFormatSql, isShowParams, level);
|
||||
|
||||
return new SqlLogFilter(sqlLog);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从map中获取字符串值
|
||||
*
|
||||
* @param map 源map
|
||||
* @param keys 多个键
|
||||
* @return 字符串值
|
||||
*/
|
||||
private String getAndRemoveString(Map<String, Object> map, String... keys) {
|
||||
Object value = null;
|
||||
for (String key : keys) {
|
||||
value = map.remove(key);
|
||||
if (null != value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return StrUtil.toStringOrNull(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从map中获取布尔值
|
||||
*
|
||||
* @param map 源map
|
||||
* @param keys 多个键
|
||||
* @return 布尔值
|
||||
*/
|
||||
private Boolean getAndRemoveBoolean(Map<String, Object> map, String... keys) {
|
||||
Object value = null;
|
||||
for (String key : keys) {
|
||||
value = map.remove(key);
|
||||
if (null != value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ConvertUtil.toBoolean(value);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package cn.hutool.v7.db.config;
|
||||
|
||||
import cn.hutool.v7.core.lang.Console;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
public class TomlConfigParserTest {
|
||||
@Test
|
||||
public void parseTest() {
|
||||
DbConfig dbConfig = TomlConfigParser.of().parse("");
|
||||
assertEquals("org.sqlite.JDBC", dbConfig.getDriver());
|
||||
assertEquals("jdbc:sqlite:test.db", dbConfig.getUrl());
|
||||
assertNull(dbConfig.getUser());
|
||||
assertNull(dbConfig.getPass());
|
||||
|
||||
assertEquals(1, dbConfig.getConnProps().size());
|
||||
assertEquals("true", dbConfig.getConnProps().getProperty("remarks"));
|
||||
assertNull(dbConfig.getPoolProps());
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseOrclTest(){
|
||||
DbConfig dbConfig = TomlConfigParser.of().parse("orcl");
|
||||
Console.log(dbConfig);
|
||||
|
||||
assertEquals("oracle.jdbc.OracleDriver", dbConfig.getDriver());
|
||||
assertEquals("jdbc:oracle:thin:@//localhost:1521/XEPDB1", dbConfig.getUrl());
|
||||
assertEquals("system", dbConfig.getUser());
|
||||
assertEquals("123456", dbConfig.getPass());
|
||||
|
||||
assertEquals(1, dbConfig.getConnProps().size());
|
||||
assertEquals("true", dbConfig.getConnProps().getProperty("remarks"));
|
||||
assertNull(dbConfig.getPoolProps());
|
||||
}
|
||||
}
|
||||
115
hutool-db/src/test/resources/config/db.toml
Normal file
115
hutool-db/src/test/resources/config/db.toml
Normal file
@ -0,0 +1,115 @@
|
||||
#
|
||||
# Copyright (c) 2013-2025 Hutool Team and hutool.cn
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# suppress inspection "Annotator" for whole file
|
||||
#===================================================================
|
||||
# 数据库配置文件样例
|
||||
# DsFactory默认读取的配置文件是config/db.setting
|
||||
# db.setting的配置包括两部分:基本连接信息和连接池配置信息。
|
||||
# 基本连接信息所有连接池都支持,连接池配置信息根据不同的连接池,连接池配置是根据连接池相应的配置项移植而来
|
||||
#===================================================================
|
||||
|
||||
## 打印SQL的配置
|
||||
# 是否在日志中显示执行的SQL,默认false
|
||||
showSql = true
|
||||
# 是否格式化显示的SQL,默认false
|
||||
formatSql = true
|
||||
# 是否显示SQL参数,默认false
|
||||
showParams = true
|
||||
# 打印SQL的日志等级,默认debug
|
||||
sqlLevel = "debug"
|
||||
|
||||
# 默认数据源
|
||||
url = "jdbc:sqlite:test.db"
|
||||
remarks = true
|
||||
|
||||
# 测试数据源
|
||||
[test]
|
||||
url = "jdbc:sqlite:test.db"
|
||||
remarks = true
|
||||
driver = "org.sqlite.JDBC"
|
||||
|
||||
# 测试用HSQLDB数据库
|
||||
[hsqldb]
|
||||
url = "jdbc:hsqldb:mem:mem_hutool"
|
||||
user = "SA"
|
||||
pass = ""
|
||||
remarks = true
|
||||
|
||||
# 测试用HSQLDB数据库
|
||||
[h2]
|
||||
url = "jdbc:h2:mem:h2_hutool"
|
||||
user = "sa"
|
||||
pass = ""
|
||||
remarks = true
|
||||
|
||||
# 测试用HSQLDB数据库
|
||||
[derby]
|
||||
url = "jdbc:derby:.derby/test_db;create=true"
|
||||
remarks = true
|
||||
|
||||
# 测试用Oracle数据库
|
||||
[orcl]
|
||||
url = "jdbc:oracle:thin:@//localhost:1521/XEPDB1"
|
||||
user = "system"
|
||||
pass = "123456"
|
||||
remarks = true
|
||||
|
||||
[mysql]
|
||||
url = "jdbc:mysql://Looly.centos8:3306/hutool_test?useSSL=false"
|
||||
user = "root"
|
||||
pass = "123456"
|
||||
remarks = true
|
||||
|
||||
[mariadb_local]
|
||||
url = "jdbc:mysql://localhost:3306/test?useSSL=false"
|
||||
user = "root"
|
||||
pass = "123456"
|
||||
remarks = true
|
||||
|
||||
[postgre]
|
||||
url = "jdbc:postgresql://localhost:5432/test_hutool"
|
||||
user = "postgres"
|
||||
pass = "123456"
|
||||
remarks = true
|
||||
|
||||
[sqlserver]
|
||||
url = "jdbc:sqlserver://Looly.database.chinacloudapi.cn:1433;database=test;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.chinacloudapi.cn;loginTimeout=30;"
|
||||
user = "Looly@Looly"
|
||||
pass = "123"
|
||||
remarks = true
|
||||
|
||||
# 测试用达梦8数据库
|
||||
# 测试环境使用docker启动,见:https://eco.dameng.com/document/dm/zh-cn/start/dm-install-docker.html
|
||||
[dm]
|
||||
url = "jdbc:dm://localhost:5236"
|
||||
user = "SYSDBA"
|
||||
pass = "SYSDBA001"
|
||||
remarks = true
|
||||
|
||||
# OceanBase
|
||||
# 测试环境使用docker启动,见:https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000217958
|
||||
[ob]
|
||||
url = "jdbc:oceanbase://localhost:2881/test"
|
||||
user = "root"
|
||||
pass = "123456"
|
||||
remarks = true
|
||||
|
||||
[hana]
|
||||
url = "jdbc:sap://localhost:30015/HAP_CONN?autoReconnect=true"
|
||||
user = "DB"
|
||||
pass = "123456"
|
||||
remarks = "true"
|
||||
Loading…
x
Reference in New Issue
Block a user