diff --git a/src/main/java/minevalley/core/api/Core.java b/src/main/java/minevalley/core/api/Core.java index 4b1e88d..941ec72 100644 --- a/src/main/java/minevalley/core/api/Core.java +++ b/src/main/java/minevalley/core/api/Core.java @@ -4,6 +4,7 @@ import minevalley.core.api.armorstand.FakeArmorStand; import minevalley.core.api.corporations.Group; import minevalley.core.api.corporations.companies.*; +import minevalley.core.api.database.StatementBuilder; import minevalley.core.api.discord.EmbeddedMessage; import minevalley.core.api.discord.Webhook; import minevalley.core.api.economy.BankAccount; @@ -55,6 +56,7 @@ import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; +import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; @@ -174,6 +176,11 @@ public static BukkitTask runAsyncTaskPeriodically(long delay, long period, @Nonn return server.runAsyncTaskPeriodically(delay, period, task); } + @Nonnull + public static StatementBuilder prepareSQL(@Nonnull @Language("SQL") String sql) { + return server.prepareSQL(sql); + } + /** * Registers an event listener. * diff --git a/src/main/java/minevalley/core/api/CoreServer.java b/src/main/java/minevalley/core/api/CoreServer.java index 94a8b2d..51ce38e 100644 --- a/src/main/java/minevalley/core/api/CoreServer.java +++ b/src/main/java/minevalley/core/api/CoreServer.java @@ -4,6 +4,7 @@ import minevalley.core.api.armorstand.FakeArmorStand; import minevalley.core.api.corporations.Group; import minevalley.core.api.corporations.companies.*; +import minevalley.core.api.database.StatementBuilder; import minevalley.core.api.discord.EmbeddedMessage; import minevalley.core.api.discord.Webhook; import minevalley.core.api.economy.BankAccount; @@ -51,6 +52,7 @@ import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; +import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.ApiStatus; import javax.annotation.Nonnegative; @@ -80,6 +82,9 @@ public interface CoreServer { @Nonnull BukkitTask runAsyncTaskPeriodically(long delay, long period, @Nonnull Runnable runnable) throws IllegalArgumentException; + @Nonnull + StatementBuilder prepareSQL(@Nonnull @Language("SQL") String sql); + void registerListener(@Nonnull Class extends Event> cls, @Nonnull EventListener extends Event> listener) throws IllegalArgumentException; void unregisterListener(@Nonnull Class extends Event> cls, @Nonnull EventListener extends Event> listener) throws IllegalArgumentException; diff --git a/src/main/java/minevalley/core/api/database/ColumnType.java b/src/main/java/minevalley/core/api/database/ColumnType.java new file mode 100644 index 0000000..5ec19c1 --- /dev/null +++ b/src/main/java/minevalley/core/api/database/ColumnType.java @@ -0,0 +1,207 @@ +package minevalley.core.api.database; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public enum ColumnType { + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code BIT}. + */ + BIT(-7), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code TINYINT}. + */ + TINYINT(-6), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code SMALLINT}. + */ + SMALLINT(5), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code INTEGER}. + */ + INTEGER(4), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code BIGINT}. + */ + BIGINT(-5), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code FLOAT}. + */ + FLOAT(6), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code REAL}. + */ + REAL(7), + + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code DOUBLE}. + */ + DOUBLE(8), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code NUMERIC}. + */ + NUMERIC(2), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code DECIMAL}. + */ + DECIMAL(3), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code CHAR}. + */ + CHAR(1), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code VARCHAR}. + */ + VARCHAR(12), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code LONGVARCHAR}. + */ + LONGVARCHAR(-1), + + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code DATE}. + */ + DATE(91), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code TIME}. + */ + TIME(92), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code TIMESTAMP}. + */ + TIMESTAMP(93), + + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code BINARY}. + */ + BINARY(-2), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code VARBINARY}. + */ + VARBINARY(-3), + + /** + *
The constant in the Java programming language, sometimes referred + * to as a type code, that identifies the generic SQL type + * {@code LONGVARBINARY}. + */ + LONGVARBINARY(-4), + + /** + *
The constant in the Java programming language + * that identifies the generic SQL value + * {@code NULL}. + */ + NULL(0), + + /** + * The constant in the Java programming language that indicates + * that the SQL type is database-specific and + * gets mapped to a Java object that can be accessed via + * the methods {@code getObject} and {@code setObject}. + */ + OTHER(1111), + + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type + * {@code DISTINCT}. + * + * @since 1.2 + */ + DISTINCT(2001), + + + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type {@code BOOLEAN}. + * + * @since 1.4 + */ + BOOLEAN(16), + + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type {@code REF CURSOR}. + * + * @since 1.8 + */ + REF_CURSOR(2012), + + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type + * {@code TIME WITH TIMEZONE}. + * + * @since 1.8 + */ + TIME_WITH_TIMEZONE(2013), + + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type + * {@code TIMESTAMP WITH TIMEZONE}. + * + * @since 1.8 + */ + TIMESTAMP_WITH_TIMEZONE(2014); + + private final int type; +} diff --git a/src/main/java/minevalley/core/api/database/StatementBuilder.java b/src/main/java/minevalley/core/api/database/StatementBuilder.java new file mode 100644 index 0000000..5de5b87 --- /dev/null +++ b/src/main/java/minevalley/core/api/database/StatementBuilder.java @@ -0,0 +1,389 @@ +package minevalley.core.api.database; + +import minevalley.core.api.Core; +import org.jetbrains.annotations.Contract; + +import javax.annotation.Nonnull; +import java.math.BigDecimal; +import java.sql.*; +import java.util.concurrent.CompletableFuture; + +/** + * This interface covers some but not nearly all features of the {@link PreparedStatement} interface as provided by the JDBC API. + * It's designed to provide some improvements such as an asynchronous execution and a builder pattern for setting parameters to reduce boilerplate code. + * Such as the {@link PreparedStatement} interface, the results of the query can be retrieved using the {@link ResultSet} interface. + *
You can call {@link StatementBuilder#executeUpdate()} or {@link StatementBuilder#executeQuery()} multiple times with or without changing parameters in between. + *
Use {@link StatementBuilder#unwrap()} to get the underlying {@link PreparedStatement} object if you need to use a method that is not covered by this interface. + *
The following code snippet demonstrates a possible way to use this interface: + *
+ * {@code + * Core.prepareSQL("UPDATE team SET note = ? WHERE unique_id = ?") + * .setString(1, "Coolest boss ever") + * .setString(2, "e0ae458e-214c-409e-ad6d-4091a6719bb0") + * .executeUpdate(); + * } + *+ * + * @see Core#prepareSQL(String) + * @see PreparedStatement + * @see ResultSet + */ +@SuppressWarnings("unused") +public interface StatementBuilder extends AutoCloseable { + + /** + * Sets the parameter at the given index to SQL {@code NULL}. + *
+ * Note: Even though JDBC handles untyped {@code NULL} values pretty well, it is mandatory to provide the {@link ColumnType} of the parameter. + * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param type the {@link ColumnType} of the parameter + * @return this {@link StatementBuilder} object + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setNull(int parameterIndex, @Nonnull ColumnType type) throws IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@code boolean} value. + *
Representation: {@code boolean} values are converted to SQL {@code BIT} values
+ * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param bool the {@code boolean} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setBoolean(int parameterIndex, boolean bool) throws IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@code byte} value. + *Representation: {@code byte} values are converted to SQL {@code TINYINT} values
+ * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param b the {@code byte} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setByte(int parameterIndex, byte b) throws IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@code short} value. + *Representation: {@code short} values are converted to SQL {@code SMALLINT} values
+ * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param s the {@code short} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setShort(int parameterIndex, short s) throws IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@code int} value. + *Representation: {@code int} values are converted to SQL {@code INTEGER} values
+ * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param i the {@code int} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setInt(int parameterIndex, int i) throws IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@code long} value. + *Representation: {@code long} values are converted to SQL {@code BIGINT} values
+ * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param l the {@code long} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setLong(int parameterIndex, long l) throws IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@code float} value. + *Representation: {@code float} values are converted to SQL {@code FLOAT} values
+ * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param f the {@code float} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setFloat(int parameterIndex, float f) throws IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@code double} value. + *Representation: {@code double} values are converted to SQL {@code DOUBLE} values
+ * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param d the {@code double} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setDouble(int parameterIndex, double d) throws IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@link BigDecimal} value. + *Representation: {@link BigDecimal} values are converted to SQL {@code DECIMAL} values
+ *+ * Note: This method is not meant to handle {@code NULL} values. Use {@link StatementBuilder#setNull(int, ColumnType)} instead. + * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param bd the {@link BigDecimal} value to set. + * @return this {@link StatementBuilder} object + * @throws IllegalArgumentException if {@code bd} is {@code null} + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setBigDecimal(int parameterIndex, @Nonnull BigDecimal bd) throws IllegalArgumentException, IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@link String} value. + *
Representation: {@link String} values are converted to SQL {@code VARCHAR} values
+ *+ * Note: This method is not meant to handle {@code NULL} values. Use {@link StatementBuilder#setNull(int, ColumnType)} instead. + * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param s the {@link String} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalArgumentException if {@code s} is {@code null} + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setString(int parameterIndex, @Nonnull String s) throws IllegalArgumentException, IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@code byte} array. + *
Representation: {@code byte} arrays are converted to SQL {@code VARBINARY} values
+ *+ * Note: This method is not meant to handle {@code NULL} values. Use {@link StatementBuilder#setNull(int, ColumnType)} instead. + * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param bytes the {@code byte} array to set + * @return this {@link StatementBuilder} object + * @throws IllegalArgumentException if {@code bytes} is {@code null} + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setBytes(int parameterIndex, @Nonnull byte[] bytes) throws IllegalArgumentException, IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@link Date} value. + *
Representation: {@link Date} values are converted to SQL {@code DATE} values
+ *+ * Note: This method is not meant to handle {@code NULL} values. Use {@link StatementBuilder#setNull(int, ColumnType)} instead. + * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param date the {@link Date} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalArgumentException if {@code date} is {@code null} + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setDate(int parameterIndex, @Nonnull Date date) throws IllegalArgumentException, IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@link Time} value. + *
Representation: {@link Time} values are converted to SQL {@code TIME} values
+ *+ * Note: This method is not meant to handle {@code NULL} values. Use {@link StatementBuilder#setNull(int, ColumnType)} instead. + * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param time the {@link Time} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalArgumentException if {@code time} is {@code null} + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setTime(int parameterIndex, @Nonnull Time time) throws IllegalArgumentException, IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@link Timestamp} value. + *
Representation: {@link Timestamp} values are converted to SQL {@code TIMESTAMP} values
+ *+ * Note: This method is not meant to handle {@code NULL} values. Use {@link StatementBuilder#setNull(int, ColumnType)} instead. + * + * @param parameterIndex the index of the parameter to set, beginning with 1 + * @param timestamp the {@link Timestamp} value to set + * @return this {@link StatementBuilder} object + * @throws IllegalArgumentException if {@code timestamp} is {@code null} + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Nonnull + @Contract("_, _ -> this") + StatementBuilder setTimestamp(int parameterIndex, @Nonnull Timestamp timestamp) throws IllegalArgumentException, IllegalStateException, SQLException; + + /** + * Sets the parameter at the given index to the given {@link Object} value. + *
+ * Note: + *
+ * Note: + *
+ * Note: This method should be used for queries that do not return a result set. + * + * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder} + * @throws SQLException if a database access error occurs + */ + @Contract(pure = true) + void executeUpdate() throws IllegalStateException, SQLException; + + /** + * Executes the SQL query that has been built so far asynchronously. + *
+ * Note: This method should be used for queries that do not return a result set.
+ *
+ * @return a {@link CompletableFuture} that will be completed once the query has been executed
+ * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder}
+ */
+ @Nonnull
+ @Contract(pure = true)
+ CompletableFuture
+ * Note: This method should be used for queries that do not return a result set.
+ *
+ * @return the generated key
+ * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder}
+ * @throws SQLException if a database access error occurs
+ */
+ @Contract(pure = true)
+ int executeUpdateAndRetrieveKey() throws IllegalStateException, SQLException;
+
+ /**
+ * Executes the SQL query that has been built so far and retrieves the generated key asynchronously.
+ *
+ * Note: This method should be used for queries that do not return a result set.
+ *
+ * @return a {@link CompletableFuture} that will be completed once the query has been executed and the key has been retrieved
+ * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder}
+ */
+ @Nonnull
+ @Contract(pure = true)
+ CompletableFuture
+ * Note: This method should be used for queries that return a result set.
+ *
+ * @return the result set
+ * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder}
+ * @throws SQLException if a database access error occurs or the query does not return a result set
+ */
+ @Nonnull
+ @Contract(pure = true)
+ ResultSet executeQuery() throws IllegalStateException, SQLException;
+
+ /**
+ * Executes the SQL query that has been built so far and returns the result set asynchronously.
+ *
+ * Note: This method should be used for queries that return a result set.
+ *
+ * @return a {@link CompletableFuture} that will be completed once the query has been executed and the result set has been retrieved
+ * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder}
+ */
+ @Nonnull
+ @Contract(pure = true)
+ CompletableFuture
+ * Note:
+ *
+ *
+ *
+ * @return a copy of the underlying {@link PreparedStatement} object
+ * @throws IllegalStateException if this method is called on a closed {@link StatementBuilder}
+ */
+ @Nonnull
+ @Contract(pure = true)
+ PreparedStatement unwrap() throws IllegalStateException;
+}
\ No newline at end of file