mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 10:38:19 +08:00
feat(db): Add databased providers assumptions
This commit is contained in:
@@ -25,8 +25,9 @@
|
|||||||
|
|
||||||
<module>providers/yaml</module>
|
<module>providers/yaml</module>
|
||||||
<module>providers/gson</module>
|
<module>providers/gson</module>
|
||||||
<!-- <module>providers/sql</module>-->
|
<module>providers/hocon</module>
|
||||||
<!-- <module>providers/hocon</module>-->
|
<module>providers/sql</module>
|
||||||
|
<module>providers/mongodb</module>
|
||||||
|
|
||||||
<module>demo</module>
|
<module>demo</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>easyconfiguration-parent</artifactId>
|
||||||
|
<groupId>cc.carm.lib</groupId>
|
||||||
|
<version>4.0.0</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||||
|
|
||||||
|
<log4j.version>2.24.3</log4j.version>
|
||||||
|
</properties>
|
||||||
|
<artifactId>easyconfiguration-mongodb</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-core</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-gson</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mongodb</groupId>
|
||||||
|
<artifactId>mongodb-driver-sync</artifactId>
|
||||||
|
<version>5.3.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-demo</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
<version>${log4j.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<version>${log4j.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-slf4j-impl</artifactId>
|
||||||
|
<version>${log4j.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package cc.carm.lib.configuration.source.mongodb;
|
||||||
|
|
||||||
|
public class MongoSource {
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration status="WARN" packages="config.SQLConfigTest">
|
||||||
|
<Appenders>
|
||||||
|
<console name="Console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n"/>
|
||||||
|
</console>
|
||||||
|
</Appenders>
|
||||||
|
<Loggers>
|
||||||
|
<Root level="info">
|
||||||
|
<filters>
|
||||||
|
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||||
|
<RegexFilter regex=".*\$\{[^}]*\}.*" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||||
|
</filters>
|
||||||
|
<AppenderRef ref="File"/>
|
||||||
|
<appender-ref ref="Console"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
||||||
|
|
||||||
+10
-9
@@ -6,15 +6,16 @@ SQL database implementation, support for MySQL or MariaDB.
|
|||||||
```mysql
|
```mysql
|
||||||
CREATE TABLE IF NOT EXISTS conf
|
CREATE TABLE IF NOT EXISTS conf
|
||||||
(
|
(
|
||||||
`namespace` VARCHAR(32) NOT NULL, # 命名空间 (代表其属于谁,类似于单个配置文件地址的概念)
|
`namespace` VARCHAR(32) NOT NULL, # 命名空间 (代表其属于谁,类似于单个配置文件地址的概念)
|
||||||
`path` VARCHAR(96) NOT NULL, # 配置路径 (ConfigPath)
|
`path` VARCHAR(96) NOT NULL, # 配置路径 (ConfigPath)
|
||||||
`type` TINYINT UNSIGNED NOT NULL DEFAULT 0, # 数据类型 (Integer/Byte/List/Map/...)
|
`type` TINYINT UNSIGNED NOT NULL DEFAULT 0, # 数据类型 (Integer/Byte/List/Map/...)
|
||||||
`value` MEDIUMTEXT, # 配置项的值 (可能为JSON格式)
|
`value` MEDIUMTEXT, # 配置项的值 (可能为JSON格式)
|
||||||
`usage` TEXT, # 配置项的用法,本质是行内注释
|
`inline_comment` TEXT, # 配置项的用法,本质是行内注释
|
||||||
`descriptions` MEDIUMTEXT, # 配置项的描述,本质是顶部注释
|
`header_comment` MEDIUMTEXT, # 配置项的描述,本质是顶部注释
|
||||||
`version` INT UNSIGNED NOT NULL DEFAULT 0, # 配置项的版本
|
`footer_comment` MEDIUMTEXT, # 配置项的描述,本质是顶部注释
|
||||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, # 创建时间
|
`version` INT UNSIGNED NOT NULL DEFAULT 0, # 配置项的版本
|
||||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, # 创建时间
|
||||||
|
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY (`namespace`, `path`)
|
PRIMARY KEY (`namespace`, `path`)
|
||||||
) ENGINE = InnoDB
|
) ENGINE = InnoDB
|
||||||
DEFAULT CHARSET = utf8mb4;
|
DEFAULT CHARSET = utf8mb4;
|
||||||
|
|||||||
+22
-2
@@ -2,13 +2,13 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>easyconfiguration-parent</artifactId>
|
<artifactId>easyconfiguration-parent</artifactId>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<version>3.9.1</version>
|
<version>4.0.0</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||||
@@ -27,6 +27,26 @@
|
|||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-feature-commentable</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-feature-section</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-feature-versioned</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
package cc.carm.lib.configuration;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.sql.SQLConfigProvider;
|
|
||||||
import cc.carm.lib.easysql.api.SQLManager;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class EasyConfiguration {
|
|
||||||
|
|
||||||
private EasyConfiguration() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SQLConfigProvider from(@NotNull SQLManager sqlManager, @NotNull String tableName, @NotNull String namespace) {
|
|
||||||
SQLConfigProvider provider = new SQLConfigProvider(sqlManager, tableName, namespace);
|
|
||||||
try {
|
|
||||||
provider.initializeConfig();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SQLConfigProvider from(@NotNull SQLManager sqlManager, @NotNull String tableName) {
|
|
||||||
return from(sqlManager, tableName, "base");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package cc.carm.lib.configuration.source.sql;
|
||||||
|
|
||||||
|
public interface SQLOptions {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package cc.carm.lib.configuration.source.sql;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||||
|
import cc.carm.lib.configuration.source.section.MemorySection;
|
||||||
|
import cc.carm.lib.easysql.api.SQLManager;
|
||||||
|
import cc.carm.lib.easysql.api.SQLTable;
|
||||||
|
import cc.carm.lib.easysql.api.enums.IndexType;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class SQLSource extends ConfigureSource<MemorySection, Map<?, ?>, SQLSource> {
|
||||||
|
|
||||||
|
protected static final @NotNull Gson DEFAULT_GSON = new GsonBuilder()
|
||||||
|
.serializeNulls().disableHtmlEscaping().setPrettyPrinting()
|
||||||
|
.create();
|
||||||
|
|
||||||
|
protected final @NotNull Gson gson;
|
||||||
|
protected final @NotNull SQLManager sqlManager;
|
||||||
|
protected final @NotNull String namespace;
|
||||||
|
protected final @NotNull SQLTable table;
|
||||||
|
|
||||||
|
protected final @NotNull Set<String> updated = new HashSet<>();
|
||||||
|
protected MemorySection rootSection;
|
||||||
|
|
||||||
|
public SQLSource(@NotNull ConfigurationHolder<? extends SQLSource> holder, long lastUpdateMillis,
|
||||||
|
@NotNull Gson gson, @NotNull SQLManager sqlManager, @NotNull String tableName, @NotNull String namespace) {
|
||||||
|
super(holder, lastUpdateMillis);
|
||||||
|
this.gson = gson;
|
||||||
|
this.sqlManager = sqlManager;
|
||||||
|
this.namespace = namespace;
|
||||||
|
this.table = SQLTable.of(tableName, builder -> {
|
||||||
|
|
||||||
|
builder.addColumn("namespace", "VARCHAR(32) NOT NULL");
|
||||||
|
builder.addColumn("path", "VARCHAR(96) NOT NULL");
|
||||||
|
|
||||||
|
builder.addColumn("type", "TINYINT NOT NULL DEFAULT 0");
|
||||||
|
builder.addColumn("value", "TEXT");
|
||||||
|
|
||||||
|
builder.addColumn("inline_comment", "TEXT");
|
||||||
|
builder.addColumn("header_comments", "MEDIUMTEXT");
|
||||||
|
builder.addColumn("footer_comments", "MEDIUMTEXT");
|
||||||
|
|
||||||
|
builder.addColumn("version", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0");
|
||||||
|
|
||||||
|
builder.addColumn("create_time", "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
||||||
|
builder.addColumn("update_time", "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
||||||
|
|
||||||
|
builder.setIndex(
|
||||||
|
IndexType.PRIMARY_KEY, "pk_" + tableName.toLowerCase(),
|
||||||
|
"namespace", "path"
|
||||||
|
);
|
||||||
|
builder.setTableSettings("ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SQLSource self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Map<?, ?> original() {
|
||||||
|
return section().data();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull MemorySection section() {
|
||||||
|
return Objects.requireNonNull(this.rootSection, "Root section is not initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() throws Exception {
|
||||||
|
if (this.updated.isEmpty()) return; // Nothing to save
|
||||||
|
|
||||||
|
Date date = new Date(); // Update time
|
||||||
|
List<Object[]> values = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String path : this.updated) {
|
||||||
|
Object value = get(path);
|
||||||
|
|
||||||
|
// if (value instanceof SQLConfigWrapper) {
|
||||||
|
// value = getSourceMap(((SQLConfigWrapper) value).getSource());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// SQLValueResolver<?> type = SQLValueTypes.get(value.getClass());
|
||||||
|
// if (type != null) {
|
||||||
|
// values.add(new Object[]{
|
||||||
|
// this.namespace, path, date,
|
||||||
|
// type.getID(), type.serializeObject(value),
|
||||||
|
// getComments().getInlineComment(path),
|
||||||
|
// GSON.toJson(getComments().getHeaderComment(path))
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updated.clear();
|
||||||
|
|
||||||
|
this.table.createReplaceBatch()
|
||||||
|
.setColumnNames("namespace", "path", "update_time", "type", "value", "inline_comment", "header_comments")
|
||||||
|
.setAllParams(values)
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onReload() throws Exception {
|
||||||
|
LinkedHashMap<String, Object> values = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// try (SQLQuery query = this.table.createQuery()
|
||||||
|
// .addCondition("namespace", namespace)
|
||||||
|
// .build().execute()) {
|
||||||
|
// ResultSet rs = query.getResultSet();
|
||||||
|
// while (rs.next()) {
|
||||||
|
// String path = rs.getString("path");
|
||||||
|
// int type = rs.getInt("type");
|
||||||
|
// try {
|
||||||
|
// SQLValueResolver<?> resolver = SQLValueTypes.get(type);
|
||||||
|
// if (resolver == null) throw new IllegalStateException("No resolver for type #" + type);
|
||||||
|
// String value = rs.getString("value");
|
||||||
|
// values.put(path, resolver.resolve(value));
|
||||||
|
//
|
||||||
|
// loadInlineComment(path, rs.getString("inline_comment"));
|
||||||
|
// loadHeaderComment(path, rs.getString("header_comments"));
|
||||||
|
// } catch (Exception ex) {
|
||||||
|
// ex.printStackTrace();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// this.rootConfiguration = new SQLConfigWrapper(this, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.sql;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
|
||||||
import cc.carm.lib.easysql.api.SQLManager;
|
|
||||||
import cc.carm.lib.easysql.api.SQLQuery;
|
|
||||||
import cc.carm.lib.easysql.api.SQLTable;
|
|
||||||
import cc.carm.lib.easysql.api.enums.IndexType;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class SQLConfigProvider extends ConfigurationProvider<SQLConfigWrapper> {
|
|
||||||
|
|
||||||
public static Gson GSON = new GsonBuilder().serializeNulls().disableHtmlEscaping().setPrettyPrinting().create();
|
|
||||||
|
|
||||||
protected final @NotNull SQLManager sqlManager;
|
|
||||||
protected final @NotNull SQLTable table;
|
|
||||||
protected final @NotNull String namespace;
|
|
||||||
|
|
||||||
protected ConfigInitializer<SQLConfigProvider> initializer;
|
|
||||||
protected ConfigurationComments comments = new ConfigurationComments();
|
|
||||||
protected SQLConfigWrapper rootConfiguration;
|
|
||||||
|
|
||||||
protected final @NotNull Set<String> updated = new HashSet<>();
|
|
||||||
|
|
||||||
public SQLConfigProvider(@NotNull SQLManager sqlManager, @NotNull String tableName, @NotNull String namespace) {
|
|
||||||
this.sqlManager = sqlManager;
|
|
||||||
this.table = SQLTable.of(tableName, builder -> {
|
|
||||||
|
|
||||||
builder.addColumn("namespace", "VARCHAR(32) NOT NULL");
|
|
||||||
builder.addColumn("path", "VARCHAR(96) NOT NULL");
|
|
||||||
|
|
||||||
builder.addColumn("type", "TINYINT NOT NULL DEFAULT 0");
|
|
||||||
builder.addColumn("value", "TEXT");
|
|
||||||
|
|
||||||
builder.addColumn("inline_comment", "TEXT");
|
|
||||||
builder.addColumn("header_comments", "MEDIUMTEXT");
|
|
||||||
|
|
||||||
builder.addColumn("create_time", "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
|
||||||
builder.addColumn("update_time", "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
builder.setIndex(
|
|
||||||
IndexType.PRIMARY_KEY, "pk_" + tableName.toLowerCase(),
|
|
||||||
"namespace", "path"
|
|
||||||
);
|
|
||||||
builder.setTableSettings("ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
||||||
});
|
|
||||||
this.namespace = namespace;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull SQLConfigWrapper getConfiguration() {
|
|
||||||
return rootConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initializeConfig() throws Exception {
|
|
||||||
this.table.create(this.sqlManager);
|
|
||||||
this.initializer = new ConfigInitializer<>(this);
|
|
||||||
onReload();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onReload() throws Exception {
|
|
||||||
this.comments = new ConfigurationComments();
|
|
||||||
LinkedHashMap<String, Object> values = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
try (SQLQuery query = this.table.createQuery()
|
|
||||||
.addCondition("namespace", namespace)
|
|
||||||
.build().execute()) {
|
|
||||||
ResultSet rs = query.getResultSet();
|
|
||||||
while (rs.next()) {
|
|
||||||
String path = rs.getString("path");
|
|
||||||
int type = rs.getInt("type");
|
|
||||||
try {
|
|
||||||
SQLValueResolver<?> resolver = SQLValueTypes.get(type);
|
|
||||||
if (resolver == null) throw new IllegalStateException("No resolver for type #" + type);
|
|
||||||
String value = rs.getString("value");
|
|
||||||
values.put(path, resolver.resolve(value));
|
|
||||||
|
|
||||||
loadInlineComment(path, rs.getString("inline_comment"));
|
|
||||||
loadHeaderComment(path, rs.getString("header_comments"));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rootConfiguration = new SQLConfigWrapper(this, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save() throws Exception {
|
|
||||||
if (this.updated.isEmpty()) return;
|
|
||||||
|
|
||||||
Date date = new Date();
|
|
||||||
List<Object[]> values = new ArrayList<>();
|
|
||||||
List<String> deletes = new ArrayList<>();
|
|
||||||
|
|
||||||
for (String path : this.updated) {
|
|
||||||
Object value = this.rootConfiguration.get(path);
|
|
||||||
if (value == null) {
|
|
||||||
deletes.add(path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value instanceof SQLConfigWrapper) {
|
|
||||||
value = getSourceMap(((SQLConfigWrapper) value).getSource());
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLValueResolver<?> type = SQLValueTypes.get(value.getClass());
|
|
||||||
if (type != null) {
|
|
||||||
values.add(new Object[]{
|
|
||||||
this.namespace, path, date,
|
|
||||||
type.getID(), type.serializeObject(value),
|
|
||||||
getComments().getInlineComment(path),
|
|
||||||
GSON.toJson(getComments().getHeaderComment(path))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updated.clear();
|
|
||||||
|
|
||||||
this.table.createReplaceBatch()
|
|
||||||
.setColumnNames("namespace", "path", "update_time", "type", "value", "inline_comment", "header_comments")
|
|
||||||
.setAllParams(values)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
for (String path : deletes) {
|
|
||||||
this.table.createDelete()
|
|
||||||
.addCondition("namespace", this.namespace)
|
|
||||||
.addCondition("path", path)
|
|
||||||
.build().execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull ConfigurationComments getComments() {
|
|
||||||
return this.comments;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull ConfigInitializer<? extends ConfigurationProvider<SQLConfigWrapper>> getInitializer() {
|
|
||||||
return this.initializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected void loadInlineComment(String path, String comment) {
|
|
||||||
if (comment == null) return;
|
|
||||||
comment = comment.trim();
|
|
||||||
if (comment.isEmpty()) return;
|
|
||||||
|
|
||||||
this.comments.setInlineComment(path, comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void loadHeaderComment(String path, String commentJson) {
|
|
||||||
if (commentJson == null) return;
|
|
||||||
commentJson = commentJson.trim();
|
|
||||||
if (commentJson.isEmpty()) return;
|
|
||||||
|
|
||||||
List<String> headerComments = GSON.fromJson(commentJson, new TypeToken<List<String>>() {
|
|
||||||
}.getType());
|
|
||||||
if (headerComments == null || headerComments.isEmpty()) return;
|
|
||||||
|
|
||||||
this.comments.setHeaderComments(path, headerComments);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Map<String, Object> getSourceMap(Map<String, Object> map) {
|
|
||||||
Map<String, Object> source = new LinkedHashMap<>();
|
|
||||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
|
||||||
if (entry.getValue() instanceof SQLConfigWrapper) {
|
|
||||||
source.put(entry.getKey(), getSourceMap(((SQLConfigWrapper) entry.getValue()).getSource()));
|
|
||||||
} else {
|
|
||||||
source.put(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.sql;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For SQL configs, that primary path will be directly mapped to the value.
|
|
||||||
*
|
|
||||||
* @author CarmJos
|
|
||||||
*/
|
|
||||||
public class SQLConfigWrapper implements ConfigurationWrapper<Map<String, Object>> {
|
|
||||||
|
|
||||||
private static final char SEPARATOR = '.';
|
|
||||||
protected final @NotNull SQLConfigProvider provider;
|
|
||||||
protected final @NotNull Map<String, Object> data;
|
|
||||||
|
|
||||||
SQLConfigWrapper(@NotNull SQLConfigProvider provider, @NotNull Map<?, ?> map) {
|
|
||||||
this.provider = provider;
|
|
||||||
this.data = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
|
||||||
String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
|
|
||||||
|
|
||||||
if (entry.getValue() instanceof Map) {
|
|
||||||
this.data.put(key, new SQLConfigWrapper(provider, (Map<?, ?>) entry.getValue()));
|
|
||||||
} else {
|
|
||||||
this.data.put(key, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Map<String, Object> getSource() {
|
|
||||||
return this.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Set<String> getKeys(boolean deep) {
|
|
||||||
return getValues(deep).keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
|
||||||
// Deep is not supported for SQL configs.
|
|
||||||
return new LinkedHashMap<>(this.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(@NotNull String path, @Nullable Object value) {
|
|
||||||
if (value instanceof Map) {
|
|
||||||
value = new SQLConfigWrapper(this.provider, (Map<?, ?>) value);
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLConfigWrapper section = getSectionFor(path);
|
|
||||||
if (section == this) {
|
|
||||||
if (value == null) {
|
|
||||||
this.data.remove(path);
|
|
||||||
} else {
|
|
||||||
this.data.put(path, value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
section.set(getChild(path), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.provider.updated.add(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(@NotNull String path) {
|
|
||||||
return get(path) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Object get(@NotNull String path) {
|
|
||||||
SQLConfigWrapper section = getSectionFor(path);
|
|
||||||
return section == this ? data.get(path) : section.get(getChild(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isList(@NotNull String path) {
|
|
||||||
return get(path) instanceof List<?>;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable List<?> getList(@NotNull String path) {
|
|
||||||
Object val = get(path);
|
|
||||||
return (val instanceof List<?>) ? (List<?>) val : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConfigurationSection(@NotNull String path) {
|
|
||||||
return get(path) instanceof SQLConfigWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable SQLConfigWrapper getConfigurationSection(@NotNull String path) {
|
|
||||||
Object val = get(path);
|
|
||||||
return (val instanceof SQLConfigWrapper) ? (SQLConfigWrapper) val : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SQLConfigWrapper getSectionFor(String path) {
|
|
||||||
int index = path.indexOf(SEPARATOR);
|
|
||||||
if (index == -1) return this;
|
|
||||||
|
|
||||||
String root = path.substring(0, index);
|
|
||||||
Object section = this.data.computeIfAbsent(root, k -> new SQLConfigWrapper(this.provider, new LinkedHashMap<>()));
|
|
||||||
|
|
||||||
return (SQLConfigWrapper) section;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getChild(String path) {
|
|
||||||
int index = path.indexOf(SEPARATOR);
|
|
||||||
return (index == -1) ? path : path.substring(index + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.sql;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class SQLValueResolver<T> {
|
|
||||||
|
|
||||||
public static <V> SQLValueResolver<V> of(int id, @NotNull Class<V> clazz,
|
|
||||||
@NotNull ConfigDataFunction<String, V> resolver) {
|
|
||||||
return of(id, clazz, resolver, String::valueOf);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <V> SQLValueResolver<V> of(int id, @NotNull Class<V> clazz,
|
|
||||||
@NotNull ConfigDataFunction<String, V> resolver,
|
|
||||||
@NotNull Function<V, String> serializer) {
|
|
||||||
return new SQLValueResolver<>(id, clazz, resolver, serializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final int id;
|
|
||||||
protected final @NotNull Class<T> clazz;
|
|
||||||
protected final @NotNull ConfigDataFunction<String, T> resolver;
|
|
||||||
protected final @NotNull Function<T, String> serializer;
|
|
||||||
|
|
||||||
protected SQLValueResolver(int id, @NotNull Class<T> clazz,
|
|
||||||
@NotNull ConfigDataFunction<String, T> resolver,
|
|
||||||
@NotNull Function<T, String> serializer) {
|
|
||||||
this.id = id;
|
|
||||||
this.clazz = clazz;
|
|
||||||
this.resolver = resolver;
|
|
||||||
this.serializer = serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getID() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull Class<T> getClazz() {
|
|
||||||
return clazz;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTypeOf(@NotNull Class<?> clazz) {
|
|
||||||
return this.clazz.isAssignableFrom(clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull T resolve(String data) throws Exception {
|
|
||||||
return resolver.parse(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable String serialize(T value) {
|
|
||||||
return String.valueOf(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable String serializeObject(Object value) {
|
|
||||||
return isTypeOf(value.getClass()) ? serialize(clazz.cast(value)) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.sql;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
interface SQLValueTypes {
|
|
||||||
|
|
||||||
SQLValueResolver<String> STRING = SQLValueResolver.of(0, String.class, ConfigDataFunction.identity());
|
|
||||||
SQLValueResolver<Byte> BYTE = SQLValueResolver.of(1, Byte.class, Byte::parseByte);
|
|
||||||
SQLValueResolver<Short> SHORT = SQLValueResolver.of(2, Short.class, Short::parseShort);
|
|
||||||
SQLValueResolver<Integer> INTEGER = SQLValueResolver.of(3, Integer.class, Integer::parseInt);
|
|
||||||
SQLValueResolver<Long> LONG = SQLValueResolver.of(4, Long.class, Long::parseLong);
|
|
||||||
SQLValueResolver<Float> FLOAT = SQLValueResolver.of(5, Float.class, Float::parseFloat);
|
|
||||||
SQLValueResolver<Double> DOUBLE = SQLValueResolver.of(6, Double.class, Double::parseDouble);
|
|
||||||
SQLValueResolver<Boolean> BOOLEAN = SQLValueResolver.of(7, Boolean.class, Boolean::parseBoolean);
|
|
||||||
SQLValueResolver<Character> CHAR = SQLValueResolver.of(8, Character.class, s -> s.charAt(0));
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
SQLValueResolver<List> LIST = SQLValueResolver.of(10, List.class,
|
|
||||||
array -> SQLConfigProvider.GSON.fromJson(array, List.class),
|
|
||||||
list -> SQLConfigProvider.GSON.toJson(list)
|
|
||||||
);
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
SQLValueResolver<Map> SECTION = SQLValueResolver.of(20, Map.class,
|
|
||||||
section -> SQLConfigProvider.GSON.fromJson(section, LinkedHashMap.class),
|
|
||||||
section -> SQLConfigProvider.GSON.toJson(section)
|
|
||||||
);
|
|
||||||
|
|
||||||
static @NotNull SQLValueResolver<?>[] values() {
|
|
||||||
return new SQLValueResolver<?>[]{
|
|
||||||
STRING, BYTE, SHORT, INTEGER, LONG, FLOAT, DOUBLE, BOOLEAN, CHAR, LIST, SECTION
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static SQLValueResolver<?> valueOf(int index) {
|
|
||||||
return values()[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
static @Nullable SQLValueResolver<?> get(int id) {
|
|
||||||
return Arrays.stream(values()).filter(v -> v.id == id).findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static @Nullable SQLValueResolver<?> get(Class<?> typeClazz) {
|
|
||||||
return Arrays.stream(values()).filter(v -> v.isTypeOf(typeClazz)).findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package config;
|
package config;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.EasyConfiguration;
|
|
||||||
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
||||||
import cc.carm.lib.configuration.sql.SQLConfigProvider;
|
import cc.carm.lib.configuration.sql.SQLConfigProvider;
|
||||||
import cc.carm.lib.easysql.EasySQL;
|
import cc.carm.lib.easysql.EasySQL;
|
||||||
@@ -19,7 +18,7 @@ public class SQLConfigTest {
|
|||||||
public void test() {
|
public void test() {
|
||||||
BeeDataSourceConfig config = new BeeDataSourceConfig();
|
BeeDataSourceConfig config = new BeeDataSourceConfig();
|
||||||
config.setDriverClassName("org.h2.Driver");
|
config.setDriverClassName("org.h2.Driver");
|
||||||
config.setJdbcUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=MySQL;");
|
config.setJdbcUrl("jdbc:h2:file:target/test;DB_CLOSE_DELAY=-1;MODE=MySQL;");
|
||||||
SQLManager manager = EasySQL.createManager(config);
|
SQLManager manager = EasySQL.createManager(config);
|
||||||
manager.setDebugMode(true);
|
manager.setDebugMode(true);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user