QueryService implementation

Affects issues:
- Close #1117
This commit is contained in:
Rsl1122 2019-07-28 11:23:05 +03:00
parent 43a43b604c
commit 87ef84d43f
9 changed files with 348 additions and 4 deletions

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.commands.RemoveEverythingTransaction;
import com.djrapitops.plan.query.QueryServiceImplementation;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -52,6 +53,7 @@ public class ManageClearCommand extends CommandNode {
private final Locale locale;
private final Processing processing;
private final DBSystem dbSystem;
private final QueryServiceImplementation queryService;
private final ErrorHandler errorHandler;
@Inject
@ -59,6 +61,7 @@ public class ManageClearCommand extends CommandNode {
Locale locale,
Processing processing,
DBSystem dbSystem,
QueryServiceImplementation queryService,
ErrorHandler errorHandler
) {
super("clear", Permissions.MANAGE.getPermission(), CommandType.PLAYER_OR_ARGS);
@ -66,6 +69,7 @@ public class ManageClearCommand extends CommandNode {
this.locale = locale;
this.processing = processing;
this.dbSystem = dbSystem;
this.queryService = queryService;
this.errorHandler = errorHandler;
setArguments("<DB>", "[-a]");
@ -104,6 +108,7 @@ public class ManageClearCommand extends CommandNode {
sender.sendMessage(locale.getString(ManageLang.PROGRESS_START));
database.executeTransaction(new RemoveEverythingTransaction())
.get(); // Wait for completion
queryService.dataCleared();
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();

View File

@ -20,6 +20,7 @@ import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.PlayerFetchQueries;
import com.djrapitops.plan.db.access.transactions.commands.RemovePlayerTransaction;
import com.djrapitops.plan.query.QueryServiceImplementation;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -55,6 +56,7 @@ public class ManageRemoveCommand extends CommandNode {
private final Locale locale;
private final Processing processing;
private final DBSystem dbSystem;
private final QueryServiceImplementation queryService;
private final UUIDUtility uuidUtility;
private final ErrorHandler errorHandler;
@ -63,6 +65,7 @@ public class ManageRemoveCommand extends CommandNode {
Locale locale,
Processing processing,
DBSystem dbSystem,
QueryServiceImplementation queryService,
UUIDUtility uuidUtility,
ErrorHandler errorHandler
) {
@ -71,6 +74,7 @@ public class ManageRemoveCommand extends CommandNode {
this.locale = locale;
this.processing = processing;
this.dbSystem = dbSystem;
this.queryService = queryService;
this.uuidUtility = uuidUtility;
this.errorHandler = errorHandler;
@ -122,6 +126,7 @@ public class ManageRemoveCommand extends CommandNode {
sender.sendMessage(locale.getString(ManageLang.PROGRESS_START));
db.executeTransaction(new RemovePlayerTransaction(playerUUID))
.get(); // Wait for completion
queryService.playerRemoved(playerUUID);
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();

View File

@ -0,0 +1,50 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.query.QueryService;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class QueryAPIExecutable implements Executable {
private final String sql;
private final QueryService.ThrowingConsumer<PreparedStatement> statement;
public QueryAPIExecutable(
String sql,
QueryService.ThrowingConsumer<PreparedStatement> statement
) {
this.sql = sql;
this.statement = statement;
}
@Override
public boolean execute(Connection connection) {
try {
try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
statement.accept(preparedStatement);
return true;
}
} catch (SQLException e) {
throw DBOpException.forCause(sql, e);
}
}
}

View File

@ -0,0 +1,54 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.access;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.SQLDB;
import com.djrapitops.plan.query.QueryService;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class QueryAPIQuery<T> implements Query<T> {
private final QueryService.ThrowingFunction<PreparedStatement, T> performQuery;
private String sql;
public QueryAPIQuery(
String sql,
QueryService.ThrowingFunction<PreparedStatement, T> performQuery
) {
this.sql = sql;
this.performQuery = performQuery;
}
@Override
public T executeQuery(SQLDB db) {
Connection connection = null;
try {
connection = db.getConnection();
try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
return performQuery.apply(preparedStatement);
}
} catch (SQLException e) {
throw DBOpException.forCause(sql, e);
} finally {
db.returnToPool(connection);
}
}
}

View File

@ -26,6 +26,7 @@ import com.djrapitops.plan.db.access.transactions.init.RemoveOldSampledDataTrans
import com.djrapitops.plan.db.sql.tables.SessionsTable;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalPlayerResultsTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalServerResultsTransaction;
import com.djrapitops.plan.query.QueryServiceImplementation;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
@ -58,6 +59,7 @@ public class DBCleanTask extends AbsRunnable {
private final Locale locale;
private final DBSystem dbSystem;
private final PlanConfig config;
private final QueryServiceImplementation queryService;
private final ServerInfo serverInfo;
private final PluginLogger logger;
private final ErrorHandler errorHandler;
@ -67,6 +69,7 @@ public class DBCleanTask extends AbsRunnable {
PlanConfig config,
Locale locale,
DBSystem dbSystem,
QueryServiceImplementation queryService,
ServerInfo serverInfo,
PluginLogger logger,
ErrorHandler errorHandler
@ -75,6 +78,7 @@ public class DBCleanTask extends AbsRunnable {
this.dbSystem = dbSystem;
this.config = config;
this.queryService = queryService;
this.serverInfo = serverInfo;
this.logger = logger;
this.errorHandler = errorHandler;
@ -110,8 +114,9 @@ public class DBCleanTask extends AbsRunnable {
long keepActiveAfter = now - config.get(TimeSettings.DELETE_INACTIVE_PLAYERS_AFTER);
List<UUID> inactivePlayers = database.query(fetchInactivePlayerUUIDs(keepActiveAfter));
for (UUID uuid : inactivePlayers) {
database.executeTransaction(new RemovePlayerTransaction(uuid));
for (UUID playerUUID : inactivePlayers) {
database.executeTransaction(new RemovePlayerTransaction(playerUUID));
queryService.playerRemoved(playerUUID);
}
return inactivePlayers.size();
}

View File

@ -80,7 +80,7 @@ public class ExtensionServiceImplementation implements ExtensionService {
ExtensionService.ExtensionServiceHolder.set(this);
}
public void enable() {
public void register() {
extensionRegister.registerBuiltInExtensions();
}

View File

@ -0,0 +1,105 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.query;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.queries.containers.ContainerFetchQueries;
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries;
import com.djrapitops.plan.db.access.queries.schema.H2SchemaQueries;
import com.djrapitops.plan.db.access.queries.schema.MySQLSchemaQueries;
import com.djrapitops.plan.db.access.queries.schema.SQLiteSchemaQueries;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
public class CommonQueriesImplementation implements CommonQueries {
private final Database db;
CommonQueriesImplementation(Database db) {
this.db = db;
}
@Override
public long fetchPlaytime(UUID playerUUID, UUID serverUUID, long after, long before) {
// TODO Replace with single query later
PlayerContainer player = db.query(ContainerFetchQueries.fetchPlayerContainer(playerUUID));
return SessionsMutator.forContainer(player)
.filterSessionsBetween(after, before)
.filterPlayedOnServer(serverUUID)
.toPlaytime();
}
@Override
public long fetchLastSeen(UUID playerUUID, UUID serverUUID) {
// TODO Replace with single query later
PlayerContainer player = db.query(ContainerFetchQueries.fetchPlayerContainer(playerUUID));
return SessionsMutator.forContainer(player)
.filterPlayedOnServer(serverUUID)
.toLastSeen();
}
@Override
public Set<UUID> fetchServerUUIDs() {
return db.query(ServerQueries.fetchServerNames()).keySet();
}
@Override
public Optional<UUID> fetchUUIDOf(String playerName) {
return db.query(UserIdentifierQueries.fetchPlayerUUIDOf(playerName));
}
@Override
public Optional<String> fetchNameOf(UUID playerUUID) {
return db.query(UserIdentifierQueries.fetchPlayerNameOf(playerUUID));
}
@Override
public boolean doesDBHaveTable(String table) {
DBType dbType = db.getType();
switch (dbType) {
case H2:
return db.query(H2SchemaQueries.doesTableExist(table));
case SQLITE:
return db.query(SQLiteSchemaQueries.doesTableExist(table));
case MYSQL:
return db.query(MySQLSchemaQueries.doesTableExist(table));
default:
throw new IllegalStateException("Unsupported Database Type: " + dbType.getName());
}
}
@Override
public boolean doesDBHaveTableColumn(String table, String column) {
DBType dbType = db.getType();
switch (dbType) {
case H2:
return db.query(H2SchemaQueries.doesColumnExist(table, column));
case MYSQL:
return db.query(MySQLSchemaQueries.doesColumnExist(table, column));
case SQLITE:
return db.query(SQLiteSchemaQueries.doesColumnExist(table, column));
default:
throw new IllegalStateException("Unsupported Database Type: " + dbType.getName());
}
}
}

View File

@ -0,0 +1,115 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.query;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.QueryAPIExecutable;
import com.djrapitops.plan.db.access.QueryAPIQuery;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.ServerInfo;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.sql.PreparedStatement;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Future;
import java.util.function.Consumer;
@Singleton
public class QueryServiceImplementation implements QueryService {
private DBSystem dbSystem;
private ServerInfo serverInfo;
private Set<Consumer<UUID>> playerRemoveSubscribers;
private Set<VoidFunction> clearSubscribers;
@Inject
public QueryServiceImplementation(
DBSystem dbSystem,
ServerInfo serverInfo
) {
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
playerRemoveSubscribers = new HashSet<>();
clearSubscribers = new HashSet<>();
}
public void register() {
QueryService.QueryServiceHolder.set(this);
}
@Override
public String getDBType() {
Database database = dbSystem.getDatabase();
if (database == null) throw new IllegalStateException("Database has not been initialized.");
return database.getType().name();
}
@Override
public <T> T query(String sql, ThrowingFunction<PreparedStatement, T> performQuery) {
return dbSystem.getDatabase().query(new QueryAPIQuery<>(sql, performQuery));
}
@Override
public Future<?> execute(String sql, ThrowingConsumer<PreparedStatement> performStatement) {
return dbSystem.getDatabase().executeTransaction(
new Transaction() {
@Override
protected void performOperations() {
execute(new QueryAPIExecutable(sql, performStatement));
}
}
);
}
@Override
public void subscribeToPlayerRemoveEvent(Consumer<UUID> eventListener) {
playerRemoveSubscribers.add(eventListener);
}
@Override
public void subscribeDataClearEvent(VoidFunction eventListener) {
clearSubscribers.add(eventListener);
}
public void playerRemoved(UUID playerUUID) {
playerRemoveSubscribers.forEach(subscriber -> subscriber.accept(playerUUID));
}
public void dataCleared() {
clearSubscribers.forEach(VoidFunction::apply);
}
@Override
public Optional<UUID> getServerUUID() {
return Optional.ofNullable(serverInfo.getServer()).map(Server::getUuid);
}
@Override
public CommonQueries getCommonQueries() {
Database database = dbSystem.getDatabase();
if (database == null) throw new IllegalStateException("Database has not been initialized.");
return new CommonQueriesImplementation(database);
}
}

View File

@ -22,6 +22,7 @@ import com.djrapitops.plan.capability.CapabilityServiceImplementation;
import com.djrapitops.plan.data.plugin.HookHandler;
import com.djrapitops.plan.extension.ExtensionService;
import com.djrapitops.plan.extension.ExtensionServiceImplementation;
import com.djrapitops.plan.query.QueryServiceImplementation;
import com.djrapitops.plan.system.cache.CacheSystem;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.export.ExportSystem;
@ -73,6 +74,7 @@ public class PlanSystem implements SubSystem {
private final HtmlUtilities htmlUtilities;
private final HookHandler hookHandler;
private final ExtensionServiceImplementation extensionService;
private final QueryServiceImplementation queryService;
private final PlanAPI planAPI;
private final ErrorHandler errorHandler;
@ -95,6 +97,7 @@ public class PlanSystem implements SubSystem {
HtmlUtilities htmlUtilities,
HookHandler hookHandler,
ExtensionServiceImplementation extensionService,
QueryServiceImplementation queryService,
PlanAPI planAPI,
ErrorHandler errorHandler
) {
@ -115,6 +118,7 @@ public class PlanSystem implements SubSystem {
this.htmlUtilities = htmlUtilities;
this.hookHandler = hookHandler;
this.extensionService = extensionService;
this.queryService = queryService;
this.planAPI = planAPI;
this.errorHandler = errorHandler;
}
@ -140,7 +144,8 @@ public class PlanSystem implements SubSystem {
taskSystem,
hookHandler
);
extensionService.enable();
extensionService.register();
queryService.register();
enabled = true;
}