Skip to content

Commit e3e018c

Browse files
committed
Clean up the logic surrounding save/load
The new load and save logic improvements, so it is more consistent.
1 parent 7c28f2b commit e3e018c

8 files changed

Lines changed: 327 additions & 154 deletions

File tree

Database/src/main/java/org/broken/arrow/database/library/builders/wrappers/LoadSetup.java

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class LoadSetup<T extends ConfigurationSerializable> {
1515

1616
private final DatabaseQueryHandler<LoadDataWrapper<T>> databaseHandler;
1717
private Consumer<DatabaseSettingsLoad> settings;
18-
private Consumer<LoadDataWrapper<T>> context;
18+
1919

2020
public LoadSetup(DatabaseQueryHandler<LoadDataWrapper<T>> databaseHandler) {
2121
this.databaseHandler = databaseHandler;
@@ -30,17 +30,6 @@ public void configure(final Consumer<DatabaseSettingsLoad> settings) {
3030
this.settings = settings;
3131
}
3232

33-
/**
34-
* Sets the logic to prepare query data for each entry in the cache.
35-
* Don't forget to define your primary key(s) or equivalent identifiers not present
36-
* in your {@link ConfigurationSerializable} implementation, by using
37-
* {@link SaveRecord#addKeys(String, Object)}.
38-
*
39-
* @param queryResult a consumer that populates or modifies a {@link LoadDataWrapper} for loading.
40-
*/
41-
public void forEachLoadedQuery(final Consumer<LoadDataWrapper<T>> queryResult) {
42-
this.context = queryResult;
43-
}
4433

4534
/**
4635
* Applies the configured database settings if present.
@@ -53,16 +42,7 @@ public void applyConfigure(final DatabaseSettingsLoad settings) {
5342
this.settings.accept(settings);
5443
}
5544

56-
/**
57-
* Applies the query preparation logic for a given loaded value if it set and data is found.
58-
*
59-
* @param loadDataWrapper the {@link LoadDataWrapper} instance to apply found query data to.
60-
*/
61-
public void applyWrapper(final LoadDataWrapper<T> loadDataWrapper) {
62-
if (this.context == null)
63-
return;
64-
context.accept(loadDataWrapper);
65-
}
45+
6646

6747
public DatabaseQueryHandler<LoadDataWrapper<T>> getDatabaseHandler() {
6848
return databaseHandler;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.broken.arrow.database.library.builders.wrappers;
2+
3+
import org.broken.arrow.database.library.builders.LoadDataWrapper;
4+
import org.broken.arrow.database.library.core.SQLDatabaseQuery;
5+
import org.broken.arrow.serialize.library.utility.serialize.ConfigurationSerializable;
6+
7+
import javax.annotation.Nonnull;
8+
import java.util.function.Consumer;
9+
10+
public class QueryContext<T> {
11+
12+
@Nonnull
13+
private final SQLDatabaseQuery sqlDatabaseQuery;
14+
@Nonnull
15+
private final String tableName;
16+
17+
private Consumer<T> context;
18+
19+
public QueryContext(@Nonnull final SQLDatabaseQuery sqlDatabaseQuery, @Nonnull final String tableName) {
20+
this.sqlDatabaseQuery = sqlDatabaseQuery;
21+
this.tableName = tableName;
22+
}
23+
24+
@Nonnull
25+
public SQLDatabaseQuery getSqlDatabaseQuery() {
26+
return sqlDatabaseQuery;
27+
}
28+
29+
@Nonnull
30+
public String getTableName() {
31+
return tableName;
32+
}
33+
34+
/**
35+
* Sets the logic to prepare or handle query data for each entry in the cache.
36+
* <p>
37+
* This method is used by both saving and loading processes, and its behavior depends
38+
* on the surrounding context (i.e., the subclass using it).
39+
* </p>
40+
*
41+
* <h3>When used for saving:</h3>
42+
* You should define primary key(s) or other identifying values not included in your
43+
* {@link ConfigurationSerializable} implementation by calling {@link SaveRecord#addKeys(String, Object)}.
44+
*
45+
* <h3>When used for loading:</h3>
46+
* Ensure your {@link LoadDataWrapper} instance can provide identifying key/value pairs.
47+
* You can access selected (or filtered if set) columns via {@link LoadDataWrapper#getFilteredMap()}.
48+
*
49+
* @param query a consumer that modifies a {@link SaveRecord} during saving, or uses a {@link LoadDataWrapper} during loading.
50+
*/
51+
public void forEachQuery(final Consumer<T> query) {
52+
this.context = query;
53+
}
54+
55+
56+
/**
57+
* Applies the query preparation logic for a given save context if present.
58+
*
59+
* @param queryResult the result for this query, can be either loading or saving action.
60+
* @return the {@code SaveRecord} instance you put in.
61+
*/
62+
public T applyQuery(final T queryResult) {
63+
if (this.context != null)
64+
context.accept(queryResult);
65+
return queryResult;
66+
}
67+
68+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package org.broken.arrow.database.library.builders.wrappers;
2+
3+
import org.broken.arrow.database.library.builders.LoadDataWrapper;
4+
import org.broken.arrow.database.library.construct.query.QueryBuilder;
5+
import org.broken.arrow.database.library.construct.query.columnbuilder.Column;
6+
import org.broken.arrow.database.library.construct.query.columnbuilder.ColumnBuilder;
7+
import org.broken.arrow.database.library.construct.query.utlity.QueryDefinition;
8+
import org.broken.arrow.database.library.core.Database;
9+
import org.broken.arrow.database.library.core.SQLDatabaseQuery;
10+
import org.broken.arrow.logging.library.Logging;
11+
import org.broken.arrow.serialize.library.utility.serialize.ConfigurationSerializable;
12+
13+
import javax.annotation.Nonnull;
14+
import java.sql.PreparedStatement;
15+
import java.sql.ResultSet;
16+
import java.sql.SQLException;
17+
import java.util.HashMap;
18+
import java.util.List;
19+
import java.util.Map;
20+
import java.util.function.Consumer;
21+
import java.util.logging.Level;
22+
23+
import static org.broken.arrow.logging.library.Logging.of;
24+
25+
public class QueryLoader<T extends ConfigurationSerializable> extends QueryContext<LoadDataWrapper<T>> {
26+
@Nonnull
27+
private final Logging log = new Logging(QueryLoader.class);
28+
@Nonnull
29+
private final Class<T> clazz;
30+
@Nonnull
31+
private final Consumer<LoadSetup<T>> setup;
32+
@Nonnull
33+
private final DatabaseSettingsLoad databaseSettings;
34+
@Nonnull
35+
private final DatabaseQueryHandler<LoadDataWrapper<T>> databaseQueryHandler;
36+
37+
38+
public QueryLoader(@Nonnull final SQLDatabaseQuery sqlDatabaseQuery, @Nonnull final String tableName, @Nonnull final Class<T> clazz, @Nonnull final Consumer<LoadSetup<T>> setup) {
39+
super(sqlDatabaseQuery, tableName);
40+
this.clazz = clazz;
41+
this.setup = setup;
42+
this.databaseSettings = new DatabaseSettingsLoad(tableName);
43+
this.databaseQueryHandler = new DatabaseQueryHandler<>(databaseSettings);
44+
}
45+
46+
public void load() {
47+
this.executeLoadQuery();
48+
}
49+
50+
/**
51+
* If you don't want to get data in the {@link #forEachQuery(Consumer)} you can get all processed values here.
52+
*
53+
* @return list with all found rows and the filtered keys if it set.
54+
*/
55+
@Nonnull
56+
public List<LoadDataWrapper<T>> getCachedResult() {
57+
return databaseQueryHandler.getData();
58+
}
59+
60+
61+
private void executeLoadQuery() {
62+
final LoadSetup<T> loadSetup = new LoadSetup<>(databaseQueryHandler);
63+
this.setup.accept(loadSetup);
64+
loadSetup.applyConfigure(databaseSettings);
65+
66+
final QueryBuilder selectTableBuilder = this.databaseQueryHandler.getQueryBuilder();
67+
SQLDatabaseQuery databaseQuery = this.getSqlDatabaseQuery();
68+
if (selectTableBuilder == null) {
69+
log.log(Level.WARNING, () -> of("The query is not set: " + databaseQueryHandler + ". Make sure you set your query into the consumer."));
70+
return;
71+
}
72+
73+
databaseQuery.executeQuery(QueryDefinition.of(selectTableBuilder), statementWrapper -> {
74+
PreparedStatement preparedStatement = statementWrapper.getContextResult();
75+
76+
if (!selectTableBuilder.getQueryModifier().getWhereBuilder().isEmpty()) {
77+
selectTableBuilder.getValues().forEach((index, value) -> {
78+
try {
79+
preparedStatement.setObject(index, value);
80+
} catch (SQLException e) {
81+
log.log(Level.WARNING, e, () -> of("Failed to set where clause values. The values that could not be executed: " + selectTableBuilder.getValues() + ". Check the stacktrace."));
82+
}
83+
});
84+
}
85+
86+
try (ResultSet resultSet = preparedStatement.executeQuery()) {
87+
while (resultSet.next()) {
88+
final ColumnBuilder<Column, Void> selectBuilder = selectTableBuilder.getQueryModifier().getSelectBuilder();
89+
Database database = databaseQuery.getDatabase();
90+
91+
final Map<String, Object> dataFromDB = database.getDataFromDB(resultSet, selectBuilder.getColumns());
92+
final T deserialize = database.deSerialize(this.clazz, dataFromDB);
93+
final Map<String, Object> columnsFiltered = getColumnsFiltered(selectBuilder, databaseQueryHandler, dataFromDB);
94+
final LoadDataWrapper<T> loadDataWrapper = new LoadDataWrapper<>(columnsFiltered, deserialize);
95+
96+
this.applyQuery(loadDataWrapper);
97+
databaseQueryHandler.add(loadDataWrapper);
98+
}
99+
} catch (SQLException e) {
100+
log.log(Level.WARNING, e, () -> of("Could not load all data for this table '" + this.getTableName() + "'. Check the stacktrace."));
101+
}
102+
});
103+
}
104+
105+
@Nonnull
106+
private <V extends ConfigurationSerializable> Map<String, Object> getColumnsFiltered(final ColumnBuilder<Column, Void> columnBuilder, DatabaseQueryHandler<LoadDataWrapper<V>> databaseQueryHandler, Map<String, Object> dataFromDB) {
107+
final List<Column> columnList = columnBuilder.getColumns();
108+
final Map<String, Object> columnsFiltered = new HashMap<>();
109+
110+
if (columnList.isEmpty()) {
111+
return columnsFiltered;
112+
}
113+
114+
for (Column column : columnList) {
115+
String columnName = column.getColumnName();
116+
if (databaseQueryHandler.containsFilteredColumn(columnName)) {
117+
Object primaryValue = dataFromDB.get(columnName);
118+
columnsFiltered.put(columnName, primaryValue);
119+
}
120+
}
121+
122+
return columnsFiltered;
123+
}
124+
125+
126+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.broken.arrow.database.library.builders.wrappers;
2+
3+
import org.broken.arrow.database.library.core.Database;
4+
import org.broken.arrow.database.library.core.SQLDatabaseQuery;
5+
import org.broken.arrow.database.library.utility.BatchExecutor;
6+
import org.broken.arrow.database.library.utility.BatchExecutorUnsafe;
7+
import org.broken.arrow.logging.library.Logging;
8+
import org.broken.arrow.serialize.library.utility.serialize.ConfigurationSerializable;
9+
10+
import javax.annotation.Nonnull;
11+
import java.sql.Connection;
12+
import java.util.List;
13+
import java.util.Map;
14+
import java.util.function.Consumer;
15+
import java.util.logging.Level;
16+
import java.util.stream.Collectors;
17+
18+
public class QuerySaver<K, V extends ConfigurationSerializable> extends QueryContext<SaveRecord<K, V>> {
19+
@Nonnull
20+
private final Logging log = new Logging(QueryLoader.class);
21+
@Nonnull
22+
private final String tableName;
23+
@Nonnull
24+
private final Map<K, V> cacheToSave;
25+
@Nonnull
26+
final Consumer<SaveSetup<K, V>> strategy;
27+
28+
29+
30+
public QuerySaver(@Nonnull final SQLDatabaseQuery sqlDatabaseQuery, @Nonnull String tableName, @Nonnull Map<K, V> cacheToSave, @Nonnull Consumer<SaveSetup<K, V>> strategy) {
31+
super(sqlDatabaseQuery,tableName);
32+
this.tableName = tableName;
33+
this.cacheToSave = cacheToSave;
34+
this.strategy = strategy;
35+
}
36+
37+
38+
public void save() {
39+
Database database = this.getSqlDatabaseQuery().getDatabase();
40+
final Connection connection = database.attemptToConnect();
41+
final BatchExecutor<SaveRecord<K, V>> batchExecutor;
42+
43+
final DatabaseSettingsSave databaseSettings = new DatabaseSettingsSave(this.tableName);
44+
final DatabaseQueryHandler<SaveRecord<K, V>> databaseQueryHandler = new DatabaseQueryHandler<>(databaseSettings);
45+
if (connection == null) {
46+
database.printFailToOpen();
47+
return;
48+
}
49+
SaveSetup<K,V> saveSetup = new SaveSetup<>();
50+
this.strategy.accept(saveSetup);
51+
saveSetup.applyConfigure(databaseSettings);
52+
53+
final List<SaveRecord<K, V>> data = cacheToSave.entrySet().stream().map(kvEntry -> this.applyQuery(new SaveRecord<>(this.tableName, kvEntry))).collect(Collectors.toList());
54+
if (data.isEmpty()) {
55+
this.log.log(Level.WARNING, () -> Logging.of("No data in the map for the table:'" + this.tableName + "' . Just must provide data and also don't forget to set your where clause."));
56+
return;
57+
}
58+
59+
if (database.isSecureQuery())
60+
batchExecutor = new BatchExecutor<>(database, connection, data);
61+
else {
62+
batchExecutor = new BatchExecutorUnsafe<>(database, connection, data);
63+
}
64+
batchExecutor.save(tableName, databaseSettings.isShallUpdate(), databaseQueryHandler);
65+
}
66+
67+
}

Database/src/main/java/org/broken/arrow/database/library/builders/wrappers/SaveSetup.java

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
public class SaveSetup<K, V extends ConfigurationSerializable> {
1515

1616
private Consumer<DatabaseSettingsSave> settings;
17-
private Consumer<SaveRecord<K, V>> context;
1817

1918
/**
2019
* Configures database-specific settings such as the table name, update flag, and option to filter out columns.
@@ -25,18 +24,6 @@ public void configure(final Consumer<DatabaseSettingsSave> settings) {
2524
this.settings = settings;
2625
}
2726

28-
/**
29-
* Sets the logic to prepare query data for each entry in the cache.
30-
* Don't forget to define your primary key(s) or equivalent identifiers not present
31-
* in your {@link ConfigurationSerializable} implementation, by using
32-
* {@link SaveRecord#addKeys(String, Object)}.
33-
*
34-
* @param query a consumer that populates or modifies a {@link SaveRecord} for saving.
35-
*/
36-
public void forEachQuery(final Consumer<SaveRecord<K, V>> query) {
37-
this.context = query;
38-
}
39-
4027
/**
4128
* Applies the configured database settings if set.
4229
*
@@ -48,16 +35,4 @@ public void applyConfigure(final DatabaseSettingsSave settings) {
4835
this.settings.accept(settings);
4936
}
5037

51-
/**
52-
* Applies the query preparation logic for a given save context if present.
53-
*
54-
* @param queryResult the {@link SaveRecord} instance to apply query logic to.
55-
* @return the {@code SaveRecord} instance you put in.
56-
*/
57-
public SaveRecord<K, V> applyQuery(final SaveRecord<K, V> queryResult) {
58-
if (this.context != null)
59-
context.accept(queryResult);
60-
return queryResult;
61-
}
62-
6338
}

0 commit comments

Comments
 (0)