diff --git a/README.md b/README.md index ca5890e..396963b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ This readme will be expanded in the future with for example code examples, currently examples can be found in the tests of the AP module and the tests of the core module. # What's left to do? -- 'complex' updates like `updateCByAAndB` which would update every row's C to the specified value where A and B match the specified value - add distinct and things like limit and offset - make 'simple' actions like `insert` more flexible - allow it to return something else than void, e.g. ~~the input entity~~ or whether there was a row added diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/RepositoryProcessor.java b/ap/src/main/java/org/geysermc/databaseutils/processor/RepositoryProcessor.java index 12a7957..e6c4678 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/RepositoryProcessor.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/RepositoryProcessor.java @@ -50,7 +50,7 @@ import org.geysermc.databaseutils.meta.Repository; import org.geysermc.databaseutils.processor.action.ActionRegistry; import org.geysermc.databaseutils.processor.query.KeywordsReader; -import org.geysermc.databaseutils.processor.query.QueryInfoCreator; +import org.geysermc.databaseutils.processor.query.QueryContextCreator; import org.geysermc.databaseutils.processor.type.RepositoryGenerator; import org.geysermc.databaseutils.processor.util.InvalidRepositoryException; import org.geysermc.databaseutils.processor.util.TypeUtils; @@ -201,9 +201,9 @@ private List processRepository(TypeElement repository) { if (action == null) { throw new InvalidRepositoryException("No available actions for %s", name); } - var queryInfo = new QueryInfoCreator(action, result, element, entity, typeUtils).create(); - action.addTo(generators, queryInfo); + var queryContext = new QueryContextCreator(action, result, element, entity, typeUtils).create(); + action.addTo(generators, queryContext); } return generators; diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/action/Action.java b/ap/src/main/java/org/geysermc/databaseutils/processor/action/Action.java index d570edc..03ea6ba 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/action/Action.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/action/Action.java @@ -31,7 +31,7 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import org.geysermc.databaseutils.processor.info.EntityInfo; -import org.geysermc.databaseutils.processor.query.QueryInfo; +import org.geysermc.databaseutils.processor.query.QueryContext; import org.geysermc.databaseutils.processor.query.section.projection.ProjectionKeywordCategory; import org.geysermc.databaseutils.processor.type.RepositoryGenerator; import org.geysermc.databaseutils.processor.util.InvalidRepositoryException; @@ -39,17 +39,23 @@ public abstract class Action { private final String actionType; - private final boolean allowSelfCollectionArgument; + private final boolean projectionColumnIsParameter; + private final boolean allowSelfParameter; + private final boolean allowReturnSelfCollection; private final boolean supportsFilter; private final List supportedProjectionCategories; protected Action( String actionType, - boolean allowSelfCollectionArgument, + boolean projectionColumnIsParameter, + boolean allowSelfParameter, + boolean allowReturnSelfCollection, boolean supportsFilter, ProjectionKeywordCategory... supportedProjectionCategories) { this.actionType = actionType; - this.allowSelfCollectionArgument = allowSelfCollectionArgument; + this.projectionColumnIsParameter = projectionColumnIsParameter; + this.allowSelfParameter = allowSelfParameter; + this.allowReturnSelfCollection = allowReturnSelfCollection; this.supportsFilter = supportsFilter; this.supportedProjectionCategories = List.of(supportedProjectionCategories); } @@ -58,8 +64,16 @@ public String actionType() { return actionType; } - public boolean allowSelfCollectionArgument() { - return allowSelfCollectionArgument; + public boolean projectionColumnIsParameter() { + return projectionColumnIsParameter; + } + + public boolean allowSelfParameter() { + return allowSelfParameter; + } + + public boolean allowReturnSelfCollection() { + return allowReturnSelfCollection; } public boolean supportsFilter() { @@ -70,7 +84,7 @@ public List unsupportedProjectionCategories() { return supportedProjectionCategories; } - protected abstract void addToSingle(RepositoryGenerator generator, QueryInfo info, MethodSpec.Builder spec); + protected abstract void addToSingle(RepositoryGenerator generator, QueryContext context, MethodSpec.Builder spec); protected boolean validateSingle( EntityInfo info, CharSequence methodName, TypeMirror returnType, TypeUtils typeUtils) { @@ -130,14 +144,16 @@ public void validate( } } - public void addTo(List generators, QueryInfo info) { - if (!info.hasBySection() && !info.parametersInfo().isNoneOrAnySelf()) { - throw new InvalidRepositoryException("Expected at most one parameter, with type %s", info.entityType()); + public void addTo(List generators, QueryContext context) { + if (!context.hasBySection() && !context.parametersInfo().isNoneOrAnySelf()) { + throw new InvalidRepositoryException("Expected at most one parameter, with type %s", context.entityType()); } for (RepositoryGenerator generator : generators) { addToSingle( - generator, info, MethodSpec.overriding(info.parametersInfo().element())); + generator, + context, + MethodSpec.overriding(context.parametersInfo().element())); } } } diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/action/DeleteAction.java b/ap/src/main/java/org/geysermc/databaseutils/processor/action/DeleteAction.java index 0594e45..d20c75d 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/action/DeleteAction.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/action/DeleteAction.java @@ -27,19 +27,19 @@ import com.squareup.javapoet.MethodSpec; import javax.lang.model.type.TypeMirror; import org.geysermc.databaseutils.processor.info.EntityInfo; -import org.geysermc.databaseutils.processor.query.QueryInfo; +import org.geysermc.databaseutils.processor.query.QueryContext; import org.geysermc.databaseutils.processor.type.RepositoryGenerator; import org.geysermc.databaseutils.processor.util.InvalidRepositoryException; import org.geysermc.databaseutils.processor.util.TypeUtils; final class DeleteAction extends Action { DeleteAction() { - super("delete", true, true); + super("delete", false, true, true, true); } @Override - protected void addToSingle(RepositoryGenerator generator, QueryInfo info, MethodSpec.Builder spec) { - generator.addDelete(info, spec); + protected void addToSingle(RepositoryGenerator generator, QueryContext context, MethodSpec.Builder spec) { + generator.addDelete(context, spec); } @Override diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/action/ExistsAction.java b/ap/src/main/java/org/geysermc/databaseutils/processor/action/ExistsAction.java index 5e02370..0426799 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/action/ExistsAction.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/action/ExistsAction.java @@ -27,7 +27,7 @@ import com.squareup.javapoet.MethodSpec; import javax.lang.model.type.TypeMirror; import org.geysermc.databaseutils.processor.info.EntityInfo; -import org.geysermc.databaseutils.processor.query.QueryInfo; +import org.geysermc.databaseutils.processor.query.QueryContext; import org.geysermc.databaseutils.processor.query.section.projection.ProjectionKeywordCategory; import org.geysermc.databaseutils.processor.type.RepositoryGenerator; import org.geysermc.databaseutils.processor.util.InvalidRepositoryException; @@ -35,7 +35,7 @@ final class ExistsAction extends Action { ExistsAction() { - super("exists", false, true, ProjectionKeywordCategory.UNIQUE); + super("exists", false, false, false, true, ProjectionKeywordCategory.UNIQUE); } @Override @@ -49,7 +49,7 @@ protected boolean validateSingle( } @Override - public void addToSingle(RepositoryGenerator generator, QueryInfo info, MethodSpec.Builder spec) { - generator.addExists(info, spec); + public void addToSingle(RepositoryGenerator generator, QueryContext context, MethodSpec.Builder spec) { + generator.addExists(context, spec); } } diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/action/FindAction.java b/ap/src/main/java/org/geysermc/databaseutils/processor/action/FindAction.java index d5c985f..88c77f6 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/action/FindAction.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/action/FindAction.java @@ -27,7 +27,7 @@ import com.squareup.javapoet.MethodSpec; import javax.lang.model.type.TypeMirror; import org.geysermc.databaseutils.processor.info.EntityInfo; -import org.geysermc.databaseutils.processor.query.QueryInfo; +import org.geysermc.databaseutils.processor.query.QueryContext; import org.geysermc.databaseutils.processor.query.section.projection.ProjectionKeywordCategory; import org.geysermc.databaseutils.processor.type.RepositoryGenerator; import org.geysermc.databaseutils.processor.util.InvalidRepositoryException; @@ -37,6 +37,8 @@ final class FindAction extends Action { FindAction() { super( "find", + false, + false, true, true, ProjectionKeywordCategory.UNIQUE, @@ -61,7 +63,7 @@ protected boolean validateCollection( } @Override - public void addToSingle(RepositoryGenerator generator, QueryInfo info, MethodSpec.Builder spec) { - generator.addFind(info, spec); + public void addToSingle(RepositoryGenerator generator, QueryContext context, MethodSpec.Builder spec) { + generator.addFind(context, spec); } } diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/action/InsertAction.java b/ap/src/main/java/org/geysermc/databaseutils/processor/action/InsertAction.java index a46d838..eb0de19 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/action/InsertAction.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/action/InsertAction.java @@ -25,16 +25,16 @@ package org.geysermc.databaseutils.processor.action; import com.squareup.javapoet.MethodSpec; -import org.geysermc.databaseutils.processor.query.QueryInfo; +import org.geysermc.databaseutils.processor.query.QueryContext; import org.geysermc.databaseutils.processor.type.RepositoryGenerator; final class InsertAction extends Action { InsertAction() { - super("insert", true, false); + super("insert", false, true, true, false); } @Override - protected void addToSingle(RepositoryGenerator generator, QueryInfo info, MethodSpec.Builder spec) { - generator.addInsert(info, spec); + protected void addToSingle(RepositoryGenerator generator, QueryContext context, MethodSpec.Builder spec) { + generator.addInsert(context, spec); } } diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/action/UpdateAction.java b/ap/src/main/java/org/geysermc/databaseutils/processor/action/UpdateAction.java index 21906a7..e122649 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/action/UpdateAction.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/action/UpdateAction.java @@ -25,16 +25,16 @@ package org.geysermc.databaseutils.processor.action; import com.squareup.javapoet.MethodSpec; -import org.geysermc.databaseutils.processor.query.QueryInfo; +import org.geysermc.databaseutils.processor.query.QueryContext; import org.geysermc.databaseutils.processor.type.RepositoryGenerator; final class UpdateAction extends Action { UpdateAction() { - super("update", true, true); + super("update", true, true, true, true); } @Override - protected void addToSingle(RepositoryGenerator generator, QueryInfo info, MethodSpec.Builder spec) { - generator.addUpdate(info, spec); + protected void addToSingle(RepositoryGenerator generator, QueryContext context, MethodSpec.Builder spec) { + generator.addUpdate(context, spec); } } diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryInfo.java b/ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryContext.java similarity index 94% rename from ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryInfo.java rename to ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryContext.java index 331a105..d0a45a7 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryInfo.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryContext.java @@ -31,13 +31,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.geysermc.databaseutils.processor.info.ColumnInfo; import org.geysermc.databaseutils.processor.info.EntityInfo; +import org.geysermc.databaseutils.processor.query.section.ProjectionSection; import org.geysermc.databaseutils.processor.query.section.factor.Factor; import org.geysermc.databaseutils.processor.query.section.factor.VariableByFactor; import org.geysermc.databaseutils.processor.query.type.ParametersTypeInfo; import org.geysermc.databaseutils.processor.query.type.ReturnTypeInfo; import org.geysermc.databaseutils.processor.util.TypeUtils; -public record QueryInfo( +public record QueryContext( EntityInfo entityInfo, KeywordsReadResult result, ParametersTypeInfo parametersInfo, @@ -91,4 +92,8 @@ public List byVariables() { public TypeMirror returnType() { return returnInfo.type(); } + + public ProjectionSection projection() { + return result.projection(); + } } diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryInfoCreator.java b/ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryContextCreator.java similarity index 88% rename from ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryInfoCreator.java rename to ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryContextCreator.java index 40b0ff3..25675dd 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryInfoCreator.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/query/QueryContextCreator.java @@ -26,7 +26,6 @@ import com.google.auto.common.MoreTypes; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; @@ -51,10 +50,10 @@ import org.geysermc.databaseutils.processor.util.TypeUtils; /** - * Analyses and validates the read Keywords and converts it into QueryInfo. + * Analyses and validates the read Keywords and converts it into QueryContext. * Note that this will edit the provided readResult. It doesn't create a new instance. */ -public class QueryInfoCreator { +public class QueryContextCreator { private final Action action; private final KeywordsReadResult readResult; private final ExecutableElement element; @@ -63,9 +62,8 @@ public class QueryInfoCreator { private final TypeMirror returnType; private final boolean async; - private final boolean isCollection; - public QueryInfoCreator( + public QueryContextCreator( Action action, KeywordsReadResult readResult, ExecutableElement element, @@ -89,25 +87,23 @@ public QueryInfoCreator( this.returnType = returnType; this.async = async; - this.isCollection = returnType != null && typeUtils.isAssignable(returnType, Collection.class); } - public QueryInfo create() { - analyseAndValidate(); - return new QueryInfo( - info, - readResult, - new ParametersTypeInfo(element, info.typeName(), typeUtils), - new ReturnTypeInfo(async, returnType, typeUtils), - typeUtils); + public QueryContext create() { + var parameterInfo = analyseValidateAndCreate(); + return new QueryContext( + info, readResult, parameterInfo, new ReturnTypeInfo(async, returnType, typeUtils), typeUtils); } - private void analyseAndValidate() { + private ParametersTypeInfo analyseValidateAndCreate() { var parameterTypes = element.getParameters().stream().map(VariableElement::asType).toList(); var parameterNames = element.getParameters().stream() .map(VariableElement::getSimpleName) .toList(); + + var parameterInfo = new ParametersTypeInfo(element, info.typeName(), typeUtils); + AtomicInteger handledInputs = new AtomicInteger(); if (readResult.projection() != null) { @@ -115,9 +111,11 @@ private void analyseAndValidate() { var handledCategories = new ArrayList(); for (@NonNull ProjectionFactor projection : readResult.projection().projections()) { + // ignore columnName factor if (projection.keyword() == null) { continue; } + var category = projection.keyword().category(); if (!handledCategories.add(category)) { throw new InvalidRepositoryException( @@ -130,6 +128,10 @@ private void analyseAndValidate() { projection.keyword().name()); } } + + if (action.projectionColumnIsParameter()) { + handledInputs.incrementAndGet(); + } } if (readResult.bySection() != null) { @@ -150,10 +152,14 @@ private void analyseAndValidate() { // if there is no By section and there are parameters, it should be the entity or the provided projection if (readResult.bySection() == null && parameterTypes.size() == 1) { - if (typeUtils.isAssignable(parameterTypes.get(0), Collection.class) - && !action.allowSelfCollectionArgument()) { + if (parameterInfo.isAnySelf() && !action.allowSelfParameter()) { + throw new InvalidRepositoryException( + "Action %s (for %s) doesn't support entity as parameter!", + action.actionType(), element.getSimpleName()); + } + if (parameterInfo.isSelfCollection() && !action.allowReturnSelfCollection()) { throw new InvalidRepositoryException( - "Action %s (for %s) doesn't support return a collection!", + "Action %s (for %s) doesn't support returning an entity collection!", action.actionType(), element.getSimpleName()); } @@ -168,12 +174,12 @@ private void analyseAndValidate() { element.getSimpleName(), column.typeName()); } }); - return; + return parameterInfo; } } action.validate(info, element.getSimpleName(), returnType, typeUtils, null); - return; + return parameterInfo; } // Otherwise the expected parameter count should equal the actual @@ -181,6 +187,7 @@ private void analyseAndValidate() { throw new IllegalStateException( "Expected %s parameters, received %s".formatted(handledInputs, parameterTypes)); } + return parameterInfo; } private void validateColumnNames( diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/query/section/by/InputKeyword.java b/ap/src/main/java/org/geysermc/databaseutils/processor/query/section/by/InputKeyword.java index bb9e16d..7938d64 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/query/section/by/InputKeyword.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/query/section/by/InputKeyword.java @@ -90,6 +90,10 @@ public void validateTypes( return parameterNames; } + public boolean isIncomplete() { + return parameterNames.size() != inputCount(); + } + public void addParameterName(@NonNull CharSequence parameterName) { parameterNames.add(parameterName); } diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/query/type/ParametersTypeInfo.java b/ap/src/main/java/org/geysermc/databaseutils/processor/query/type/ParametersTypeInfo.java index d21aad4..6b90aa6 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/query/type/ParametersTypeInfo.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/query/type/ParametersTypeInfo.java @@ -63,6 +63,10 @@ public CharSequence name(int index) { return element.getParameters().get(index).getSimpleName(); } + public boolean hasParameters() { + return !element.getParameters().isEmpty(); + } + public boolean isAnySelf() { return isSelf() || isSelfCollection(); } diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/type/RepositoryGenerator.java b/ap/src/main/java/org/geysermc/databaseutils/processor/type/RepositoryGenerator.java index 69605b2..b268201 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/type/RepositoryGenerator.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/type/RepositoryGenerator.java @@ -35,7 +35,7 @@ import org.geysermc.databaseutils.codec.TypeCodecRegistry; import org.geysermc.databaseutils.processor.info.ColumnInfo; import org.geysermc.databaseutils.processor.info.EntityInfo; -import org.geysermc.databaseutils.processor.query.QueryInfo; +import org.geysermc.databaseutils.processor.query.QueryContext; import org.geysermc.databaseutils.processor.util.TypeUtils; public abstract class RepositoryGenerator { @@ -48,15 +48,15 @@ public abstract class RepositoryGenerator { protected void onConstructorBuilder(MethodSpec.Builder builder) {} - public abstract void addFind(QueryInfo info, MethodSpec.Builder spec); + public abstract void addFind(QueryContext context, MethodSpec.Builder spec); - public abstract void addExists(QueryInfo info, MethodSpec.Builder spec); + public abstract void addExists(QueryContext context, MethodSpec.Builder spec); - public abstract void addInsert(QueryInfo info, MethodSpec.Builder spec); + public abstract void addInsert(QueryContext context, MethodSpec.Builder spec); - public abstract void addUpdate(QueryInfo info, MethodSpec.Builder spec); + public abstract void addUpdate(QueryContext context, MethodSpec.Builder spec); - public abstract void addDelete(QueryInfo info, MethodSpec.Builder spec); + public abstract void addDelete(QueryContext context, MethodSpec.Builder spec); public void init(TypeElement superType, EntityInfo entityInfo) { if (this.typeSpec != null) { diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/type/SqlRepositoryGenerator.java b/ap/src/main/java/org/geysermc/databaseutils/processor/type/SqlRepositoryGenerator.java index e88df83..49b7cd1 100644 --- a/ap/src/main/java/org/geysermc/databaseutils/processor/type/SqlRepositoryGenerator.java +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/type/SqlRepositoryGenerator.java @@ -38,13 +38,11 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.concurrent.CompletionException; import java.util.function.Supplier; import javax.lang.model.element.Modifier; -import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.databaseutils.processor.info.ColumnInfo; -import org.geysermc.databaseutils.processor.query.QueryInfo; +import org.geysermc.databaseutils.processor.query.QueryContext; import org.geysermc.databaseutils.processor.query.section.by.keyword.EqualsKeyword; import org.geysermc.databaseutils.processor.query.section.by.keyword.LessThanKeyword; import org.geysermc.databaseutils.processor.query.section.factor.AndFactor; @@ -54,10 +52,14 @@ import org.geysermc.databaseutils.processor.query.section.projection.ProjectionKeyword; import org.geysermc.databaseutils.processor.query.section.projection.keyword.AvgProjectionKeyword; import org.geysermc.databaseutils.processor.query.section.projection.keyword.TopProjectionKeyword; +import org.geysermc.databaseutils.processor.type.sql.QueryBuilder; +import org.geysermc.databaseutils.processor.type.sql.QueryBuilder.QueryBuilderColumn; import org.geysermc.databaseutils.processor.util.InvalidRepositoryException; import org.geysermc.databaseutils.processor.util.TypeUtils; public class SqlRepositoryGenerator extends RepositoryGenerator { + private static final int BATCH_SIZE = 250; + @Override protected String upperCamelCaseDatabaseType() { return "Sql"; @@ -70,18 +72,19 @@ protected void onConstructorBuilder(MethodSpec.Builder builder) { } @Override - public void addFind(QueryInfo info, MethodSpec.Builder spec) { - var query = "select %s from %s".formatted(createProjectionFor(info), info.tableName()); - if (info.hasBySection()) { - query += " where %s".formatted(createWhereForAll(info)); + public void addFind(QueryContext context, MethodSpec.Builder spec) { + var builder = new QueryBuilder(context) + .addRaw("select %s from %s", createProjectionFor(context), context.tableName()); + if (context.hasBySection()) { + builder.add("where %s", this::createWhereForFactors); } - addExecuteQueryData(spec, query, info, () -> { - if (info.returnInfo().isCollection()) { + addExecuteQueryData(spec, context, builder, () -> { + if (context.returnInfo().isCollection()) { spec.addStatement( "$T __responses = new $L<>()", - info.returnType(), - info.typeUtils().collectionImplementationFor(info.returnType())); + context.returnType(), + context.typeUtils().collectionImplementationFor(context.returnType())); spec.beginControlFlow("while (__result.next())"); } else { spec.beginControlFlow("if (!__result.next())"); @@ -90,7 +93,7 @@ public void addFind(QueryInfo info, MethodSpec.Builder spec) { } var arguments = new ArrayList(); - for (ColumnInfo column : info.columns()) { + for (ColumnInfo column : context.columns()) { var columnType = ClassName.bestGuess(column.typeName().toString()); var getFormat = jdbcGetFor(column.typeName(), "__result.%s(%s)"); @@ -103,137 +106,148 @@ public void addFind(QueryInfo info, MethodSpec.Builder spec) { arguments.add("_" + column.name()); } - if (info.returnInfo().isCollection()) { + if (context.returnInfo().isCollection()) { spec.addStatement( "__responses.add(new $T($L))", - ClassName.bestGuess(info.entityType().toString()), + ClassName.bestGuess(context.entityType().toString()), String.join(", ", arguments)); spec.endControlFlow(); spec.addStatement("return __responses"); } else { spec.addStatement( "return new $T($L)", - ClassName.bestGuess(info.entityType().toString()), + ClassName.bestGuess(context.entityType().toString()), String.join(", ", arguments)); } }); } @Override - public void addExists(QueryInfo info, MethodSpec.Builder spec) { - var query = "select 1 from %s".formatted(info.tableName()); - if (info.hasBySection()) { - query += " where %s".formatted(createWhereForAll(info)); + public void addExists(QueryContext context, MethodSpec.Builder spec) { + var builder = new QueryBuilder(context).addRaw("select 1 from %s", context.tableName()); + if (context.hasBySection()) { + builder.add("where %s", this::createWhereForFactors); } - addExecuteQueryData(spec, query, info, () -> spec.addStatement("return __result.next()")); + addExecuteQueryData(spec, context, builder, () -> spec.addStatement("return __result.next()")); } @Override - public void addInsert(QueryInfo info, MethodSpec.Builder spec) { - var columnNames = - String.join(",", info.columns().stream().map(ColumnInfo::name).toList()); - var columnParameters = String.join(",", repeat("?", info.columns().size())); - var query = "insert into %s (%s) values (%s)".formatted(info.tableName(), columnNames, columnParameters); - addUpdateQueryData(spec, query, info, info.columns()); + public void addInsert(QueryContext context, MethodSpec.Builder spec) { + var columnNames = String.join( + ",", context.columns().stream().map(ColumnInfo::name).toList()); + var columnParameters = String.join(",", repeat("?", context.columns().size())); + + var builder = new QueryBuilder(context) + .addRaw("insert into %s (%s) values (%s)", context.tableName(), columnNames, columnParameters) + .addAll(context.columns()); + addUpdateQueryData(spec, context, builder); } @Override - public void addUpdate(QueryInfo info, MethodSpec.Builder spec) { - // todo make it work with By section - var query = - "update %s set %s where %s".formatted(info.tableName(), createSetFor(info), createWhereForKeys(info)); - addUpdateQueryData(spec, query, info, info.entityInfo().notKeyFirstColumns()); + public void addUpdate(QueryContext context, MethodSpec.Builder spec) { + var builder = new QueryBuilder(context) + .addRaw("update %s", context.tableName()) + .add("set %s", this::createSetFor); + + if (context.hasBySection()) { + builder.add("where %s", this::createWhereForFactors); + } else { + builder.add("where %s", this::createWhereForKeys); + } + addUpdateQueryData(spec, context, builder); } @Override - public void addDelete(QueryInfo info, MethodSpec.Builder spec) { - if (info.hasBySection()) { - var query = "delete from %s where %s".formatted(info.tableName(), createWhereForAll(info)); - addUpdateQueryData(spec, query, info); - return; + public void addDelete(QueryContext context, MethodSpec.Builder spec) { + var builder = new QueryBuilder(context).addRaw("delete from %s", context.tableName()); + if (context.hasBySection()) { + builder.add("where %s", this::createWhereForFactors); + } else { + builder.add("where %s", this::createWhereForKeys); } - - var query = "delete from %s where %s".formatted(info.tableName(), createWhereForKeys(info)); - addUpdateQueryData(spec, query, info, info.entityInfo().keyColumns()); + addUpdateQueryData(spec, context, builder); } - private void addExecuteQueryData(MethodSpec.Builder spec, String query, QueryInfo info, Runnable content) { - addBySectionData(spec, query, info, () -> { + private void addExecuteQueryData( + MethodSpec.Builder spec, QueryContext context, QueryBuilder builder, Runnable content) { + addBySectionData(spec, context, builder, () -> { spec.beginControlFlow("try ($T __result = __statement.executeQuery())", ResultSet.class); content.run(); spec.endControlFlow(); }); } - private void addUpdateQueryData(MethodSpec.Builder spec, String query, QueryInfo info) { - addBySectionData(spec, query, info, () -> { - spec.addStatement("__statement.executeUpdate()"); - if (info.typeUtils().isType(Void.class, info.returnType())) { - spec.addStatement("return " + (info.returnInfo().async() ? "null" : "")); - } else if (info.typeUtils().isType(info.entityType(), info.returnType())) { + private void addUpdateQueryData(MethodSpec.Builder spec, QueryContext context, QueryBuilder builder) { + addBySectionData(spec, context, builder, () -> { + if (!context.parametersInfo().isSelfCollection()) { + spec.addStatement("__statement.executeUpdate()"); + } + + if (context.typeUtils().isType(Void.class, context.returnType())) { + spec.addStatement("return $L", context.returnInfo().async() ? "null" : ""); + } else if (context.typeUtils().isType(context.entityType(), context.returnType())) { // todo support also creating an entity type from the given parameters - spec.addStatement("return $L", info.parametersInfo().name(0)); + spec.addStatement("return $L", context.parametersInfo().name(0)); } else { throw new InvalidRepositoryException( - "Return type can be either void or %s but got %s", info.entityType(), info.returnType()); + "Return type can be either void or %s but got %s", context.entityType(), context.returnType()); } }); } - private void addUpdateQueryData(MethodSpec.Builder spec, String query, QueryInfo info, List columns) { - wrapInCompletableFuture(spec, info.returnInfo().async(), () -> { + private void addBySectionData( + MethodSpec.Builder spec, QueryContext context, QueryBuilder builder, Runnable execute) { + + wrapInCompletableFuture(spec, context.returnInfo().async(), () -> { spec.beginControlFlow("try ($T __connection = this.dataSource.getConnection())", Connection.class); spec.beginControlFlow( - "try ($T __statement = __connection.prepareStatement($S))", PreparedStatement.class, query); + "try ($T __statement = __connection.prepareStatement($S))", + PreparedStatement.class, + builder.query()); - var parameterName = info.parametersInfo().name(0); + CharSequence parameterName = ""; + if (context.parametersInfo().hasParameters()) { + parameterName = context.parametersInfo().name(0); + } - if (info.parametersInfo().isSelfCollection()) { + if (context.parametersInfo().isSelfCollection()) { spec.addStatement("int __count = 0"); spec.beginControlFlow("for (var __element : $L)", parameterName); parameterName = "__element"; } - // if it doesn't have a By section, we add all the requested columns int variableIndex = 0; - for (ColumnInfo column : columns) { - var columnName = column.name(); - var columnType = column.typeName(); + for (QueryBuilderColumn column : builder.columns()) { + var columnInfo = column.info(); + + CharSequence input = "%s.%s()".formatted(parameterName, columnInfo.name()); + if (column.parameterName() != null) { + input = column.parameterName(); + } - var input = "%s.%s()".formatted(parameterName, columnName); - if (TypeUtils.needsTypeCodec(columnType)) { - input = CodeBlock.of("this.__$L.encode($L)", columnName, input) + if (TypeUtils.needsTypeCodec(columnInfo.typeName())) { + input = CodeBlock.of("this.__$L.encode($L)", columnInfo.name(), input) .toString(); } // jdbc index starts at 1 - spec.addStatement(jdbcSetFor(columnType, "__statement.%s($L, $L)"), ++variableIndex, input); + spec.addStatement(jdbcSetFor(columnInfo.typeName(), "__statement.%s($L, $L)"), ++variableIndex, input); } - if (info.parametersInfo().isSelfCollection()) { + if (context.parametersInfo().isSelfCollection()) { spec.addStatement("__statement.addBatch()"); - spec.beginControlFlow("if (__count % 250 == 0)"); + spec.beginControlFlow("if (__count % $L == 0)", BATCH_SIZE); spec.addStatement("__statement.executeBatch()"); spec.endControlFlow(); spec.endControlFlow(); spec.addStatement("__statement.executeBatch()"); spec.addStatement("__connection.commit()"); - } else { - spec.addStatement("__statement.executeUpdate()"); } - if (info.typeUtils().isType(Void.class, info.returnType())) { - spec.addStatement("return " + (info.returnInfo().async() ? "null" : "")); - } else if (info.typeUtils().isType(info.entityType(), info.returnType())) { - // todo support also creating an entity type from the given parameters - spec.addStatement("return $L", info.parametersInfo().name(0)); - } else { - throw new InvalidRepositoryException( - "Return type can be either void or %s but got %s", info.entityType(), info.returnType()); - } + execute.run(); - if (info.parametersInfo().isSelfCollection()) { + if (context.parametersInfo().isSelfCollection()) { spec.nextControlFlow("catch ($T __exception)", SQLException.class); spec.addStatement("__connection.rollback()"); spec.addStatement("throw __exception"); @@ -247,54 +261,31 @@ private void addUpdateQueryData(MethodSpec.Builder spec, String query, QueryInfo typeSpec.addMethod(spec.build()); } - private void addBySectionData(MethodSpec.Builder spec, String query, QueryInfo info, Runnable content) { - wrapInCompletableFuture(spec, info.returnInfo().async(), () -> { - spec.beginControlFlow("try ($T __connection = this.dataSource.getConnection())", Connection.class); - spec.beginControlFlow( - "try ($T __statement = __connection.prepareStatement($S))", PreparedStatement.class, query); - - // if it has a By section, everything is handled through the parameters - int variableIndex = 0; - for (VariableByFactor variable : info.byVariables()) { - var columnName = variable.columnName(); - var columnType = - Objects.requireNonNull(info.columnFor(columnName)).typeName(); - - for (@NonNull CharSequence parameterName : variable.keyword().parameterNames()) { - var input = parameterName; - if (TypeUtils.needsTypeCodec(columnType)) { - input = CodeBlock.of("this.__$L.encode($L)", columnName, input) - .toString(); - } - // jdbc index starts at 1 - spec.addStatement(jdbcSetFor(columnType, "__statement.%s($L, $L)"), ++variableIndex, input); - } - } - - content.run(); - - spec.endControlFlow(); - spec.nextControlFlow("catch ($T __exception)", SQLException.class); - spec.addStatement("throw new $T($S, __exception)", CompletionException.class, "Unexpected error occurred"); - spec.endControlFlow(); - }); - typeSpec.addMethod(spec.build()); - } - - private String createSetFor(QueryInfo info) { - return createParametersForColumns(info.entityInfo().notKeyColumns(), null, ','); + private String createSetFor(QueryContext context, QueryBuilder builder) { + if (context.projection() != null && context.projection().columnName() != null) { + //noinspection DataFlowIssue + var columns = List.of(context.columnFor(context.projection().columnName())); + return createParametersForColumns(columns, null, ',', builder, true); + } + var columns = context.entityInfo().notKeyColumns(); + return createParametersForColumns(columns, null, ',', builder, false); } - private String createWhereForKeys(QueryInfo info) { - return createParametersForColumns(info.entityInfo().keyColumns(), () -> AndFactor.INSTANCE, ' '); + private String createWhereForKeys(QueryContext context, QueryBuilder builder) { + return createParametersForColumns( + context.entityInfo().keyColumns(), () -> AndFactor.INSTANCE, ' ', builder, false); } - private String createWhereForAll(QueryInfo info) { - return createParametersForFactors(info.bySectionFactors(), ' '); + private String createWhereForFactors(QueryContext context, QueryBuilder builder) { + return createParametersForFactors(context.bySectionFactors(), ' ', builder, true); } private String createParametersForColumns( - List columns, Supplier factorSeparator, char separator) { + List columns, + Supplier factorSeparator, + char separator, + QueryBuilder builder, + boolean parameter) { var factors = new ArrayList(); for (ColumnInfo column : columns) { if (!factors.isEmpty() && factorSeparator != null) { @@ -302,10 +293,11 @@ private String createParametersForColumns( } factors.add(new VariableByFactor(column.name())); } - return createParametersForFactors(factors, separator); + return createParametersForFactors(factors, separator, builder, parameter); } - private String createParametersForFactors(List factors, char separator) { + private String createParametersForFactors( + List factors, char separator, QueryBuilder queryBuilder, boolean parameter) { var builder = new StringBuilder(); for (Factor factor : factors) { if (!builder.isEmpty()) { @@ -334,12 +326,14 @@ private String createParametersForFactors(List factors, char separator) } else { throw new InvalidRepositoryException("Unsupported keyword %s", keyword); } + + queryBuilder.addColumn(variable, parameter); } return builder.toString(); } - private String createProjectionFor(QueryInfo info) { - var section = info.result().projection(); + private String createProjectionFor(QueryContext context) { + var section = context.result().projection(); if (section == null) { return "*"; } @@ -354,7 +348,7 @@ private String createProjectionFor(QueryInfo info) { for (ProjectionKeyword projection : section.notDistinctProjectionKeywords()) { if (projection instanceof AvgProjectionKeyword) { - if (!info.typeUtils().isWholeNumberType(info.returnType())) { + if (!context.typeUtils().isWholeNumberType(context.returnType())) { result = "avg(%s)".formatted(result); } continue; diff --git a/ap/src/main/java/org/geysermc/databaseutils/processor/type/sql/QueryBuilder.java b/ap/src/main/java/org/geysermc/databaseutils/processor/type/sql/QueryBuilder.java new file mode 100644 index 0000000..ca8b684 --- /dev/null +++ b/ap/src/main/java/org/geysermc/databaseutils/processor/type/sql/QueryBuilder.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024 GeyserMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/DatabaseUtils + */ +package org.geysermc.databaseutils.processor.type.sql; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.databaseutils.processor.info.ColumnInfo; +import org.geysermc.databaseutils.processor.query.QueryContext; +import org.geysermc.databaseutils.processor.query.section.factor.VariableByFactor; + +public class QueryBuilder { + private final QueryContext queryContext; + private final StringBuilder query = new StringBuilder(); + private final List columns = new ArrayList<>(); + private int parameterIndex = 0; + + public QueryBuilder(QueryContext queryContext) { + this.queryContext = queryContext; + } + + public String query() { + return query.toString(); + } + + public List columns() { + return columns; + } + + public QueryBuilder add(String queryPart, BiFunction function) { + return addRaw(queryPart, function.apply(queryContext, this)); + } + + public QueryBuilder add(String queryPart, boolean parameters, String... columnNames) { + return add(queryPart, parameters, List.of(columnNames)); + } + + public QueryBuilder add(String queryPart, boolean parameters, List columnNames) { + addRaw(queryPart, columnNames); + for (String columnName : columnNames) { + addColumn(columnName, parameters); + } + return this; + } + + public QueryBuilder addRaw(String queryPart, String... format) { + return addRaw(queryPart, List.of(format)); + } + + public QueryBuilder addRaw(String queryPart, List format) { + if (!query.isEmpty()) { + query.append(' '); + } + query.append(queryPart.formatted(format.toArray())); + return this; + } + + public QueryBuilder addColumn(CharSequence columnName, boolean parameter) { + var parameterName = parameter ? queryContext.parametersInfo().name(parameterIndex++) : null; + columns.add(new QueryBuilderColumn(queryContext.columnFor(columnName), parameterName)); + return this; + } + + public QueryBuilder addColumn(ColumnInfo info) { + columns.add(new QueryBuilderColumn(info, null)); + return this; + } + + public QueryBuilder addColumn(VariableByFactor variable, boolean parameter) { + if (variable.keyword().isIncomplete()) { + addColumn(variable.columnName(), parameter); + return this; + } + + for (@NonNull CharSequence parameterName : variable.keyword().parameterNames()) { + columns.add(new QueryBuilderColumn(queryContext.columnFor(variable.columnName()), parameterName)); + } + return this; + } + + public QueryBuilder addAll(List columns) { + columns.forEach(this::addColumn); + return this; + } + + public record QueryBuilderColumn(ColumnInfo info, CharSequence parameterName) {} +} diff --git a/ap/src/test/java/org/geysermc/databaseutils/RepositoryImplementationGenerationTest.java b/ap/src/test/java/org/geysermc/databaseutils/RepositoryImplementationGenerationTest.java index b57823c..5e4e2f9 100644 --- a/ap/src/test/java/org/geysermc/databaseutils/RepositoryImplementationGenerationTest.java +++ b/ap/src/test/java/org/geysermc/databaseutils/RepositoryImplementationGenerationTest.java @@ -36,4 +36,9 @@ class RepositoryImplementationGenerationTest { void testBasicCompilation() { testCompilation("test/basic/", "BasicRepository"); } + + @Test + void testAdvancedCompilation() { + testCompilation("test/advanced/", "AdvancedRepository"); + } } diff --git a/ap/src/test/resources/test/advanced/AdvancedRepository.java b/ap/src/test/resources/test/advanced/AdvancedRepository.java new file mode 100644 index 0000000..0b4e2a3 --- /dev/null +++ b/ap/src/test/resources/test/advanced/AdvancedRepository.java @@ -0,0 +1,15 @@ +package test.advanced; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.geysermc.databaseutils.IRepository; +import org.geysermc.databaseutils.meta.Repository; + +@Repository +public interface AdvancedRepository extends IRepository { + CompletableFuture findByAAndB(int aa, String b); + + CompletableFuture existsByAOrB(int a, String bb); + + void updateCByBAndC(String newValue, String b, String c); +} \ No newline at end of file diff --git a/ap/src/test/resources/test/advanced/AdvancedRepositorySqlImpl.java b/ap/src/test/resources/test/advanced/AdvancedRepositorySqlImpl.java new file mode 100644 index 0000000..f2a0980 --- /dev/null +++ b/ap/src/test/resources/test/advanced/AdvancedRepositorySqlImpl.java @@ -0,0 +1,87 @@ +package test.advanced; + +import com.zaxxer.hikari.HikariDataSource; +import java.lang.Boolean; +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import org.geysermc.databaseutils.codec.TypeCodec; +import org.geysermc.databaseutils.codec.TypeCodecRegistry; +import org.geysermc.databaseutils.sql.SqlDatabase; + +public final class AdvancedRepositorySqlImpl implements AdvancedRepository { + private final SqlDatabase database; + + private final HikariDataSource dataSource; + + private final TypeCodec __d; + + public AdvancedRepositorySqlImpl(SqlDatabase database, TypeCodecRegistry registry) { + this.database = database; + this.dataSource = database.dataSource(); + this.__d = registry.requireCodecFor(UUID.class); + } + + @Override + public CompletableFuture findByAAndB(int aa, String b) { + return CompletableFuture.supplyAsync(() -> { + try (Connection __connection = this.dataSource.getConnection()) { + try (PreparedStatement __statement = __connection.prepareStatement("select * from hello where a=? and b=?")) { + __statement.setInt(1, aa); + __statement.setString(2, b); + try (ResultSet __result = __statement.executeQuery()) { + if (!__result.next()) { + return null; + } + Integer _a = __result.getInt("a"); + String _b = __result.getString("b"); + String _c = __result.getString("c"); + UUID _d = this.__d.decode(__result.getBytes("d")); + return new TestEntity(_a, _b, _c, _d); + } + } + } catch (SQLException __exception) { + throw new CompletionException("Unexpected error occurred", __exception); + } + } , this.database.executorService()); + } + + @Override + public CompletableFuture existsByAOrB(int a, String bb) { + return CompletableFuture.supplyAsync(() -> { + try (Connection __connection = this.dataSource.getConnection()) { + try (PreparedStatement __statement = __connection.prepareStatement("select 1 from hello where a=? or b=?")) { + __statement.setInt(1, a); + __statement.setString(2, bb); + try (ResultSet __result = __statement.executeQuery()) { + return __result.next(); + } + } + } catch (SQLException __exception) { + throw new CompletionException("Unexpected error occurred", __exception); + } + } , this.database.executorService()); + } + + @Override + public void updateCByBAndC(String newValue, String b, String c) { + try (Connection __connection = this.dataSource.getConnection()) { + try (PreparedStatement __statement = __connection.prepareStatement("update hello set c=? where b=? and c=?")) { + __statement.setString(1, newValue); + __statement.setString(2, b); + __statement.setString(3, c); + __statement.executeUpdate(); + return ; + } + } catch (SQLException __exception) { + throw new CompletionException("Unexpected error occurred", __exception); + } + } +} diff --git a/ap/src/test/resources/test/advanced/SqlDatabaseGenerated.java b/ap/src/test/resources/test/advanced/SqlDatabaseGenerated.java new file mode 100644 index 0000000..ffe5342 --- /dev/null +++ b/ap/src/test/resources/test/advanced/SqlDatabaseGenerated.java @@ -0,0 +1,39 @@ +package org.geysermc.databaseutils.sql; + +import java.lang.Integer; +import java.lang.String; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.function.BiFunction; +import org.geysermc.databaseutils.IRepository; +import org.geysermc.databaseutils.codec.TypeCodecRegistry; +import test.advanced.AdvancedRepositorySqlImpl; + +class SqlDatabaseGenerated { + private static final boolean HAS_ASYNC = true; + + private static final List>> REPOSITORIES; + + static { + REPOSITORIES = new ArrayList<>(); + REPOSITORIES.add(AdvancedRepositorySqlImpl::new); + } + + static void createEntities(SqlDatabase database) throws SQLException { + SqlDialect dialect = database.dialect(); + try (Connection connection = database.dataSource().getConnection()) { + try (Statement statement = connection.createStatement()) { + statement.executeUpdate("CREATE TABLE IF NOT EXISTS hello (" + + "a " + SqlTypeMappingRegistry.sqlTypeFor(Integer.class, dialect) + ',' + + "b " + SqlTypeMappingRegistry.sqlTypeFor(String.class, dialect) + ',' + + "c " + SqlTypeMappingRegistry.sqlTypeFor(String.class, dialect) + ',' + + "d " + SqlTypeMappingRegistry.sqlTypeFor(UUID.class, dialect) + + ")"); + } + } + } +} \ No newline at end of file diff --git a/ap/src/test/resources/test/advanced/TestEntity.java b/ap/src/test/resources/test/advanced/TestEntity.java new file mode 100644 index 0000000..14e3cc1 --- /dev/null +++ b/ap/src/test/resources/test/advanced/TestEntity.java @@ -0,0 +1,11 @@ +package test.advanced; + +import java.util.UUID; +import org.geysermc.databaseutils.meta.Entity; +import org.geysermc.databaseutils.meta.Index; +import org.geysermc.databaseutils.meta.Key; + +@Index(columns = {"c"}) +@Entity("hello") +public record TestEntity(@Key int a, @Key String b, String c, UUID d) { +} \ No newline at end of file diff --git a/ap/src/test/resources/test/basic/BasicRepository.java b/ap/src/test/resources/test/basic/BasicRepository.java index 41d8875..9c4122b 100644 --- a/ap/src/test/resources/test/basic/BasicRepository.java +++ b/ap/src/test/resources/test/basic/BasicRepository.java @@ -1,7 +1,6 @@ package test.basic; import java.util.List; -import java.util.Set; import java.util.concurrent.CompletableFuture; import org.geysermc.databaseutils.IRepository; import org.geysermc.databaseutils.meta.Repository; @@ -10,13 +9,11 @@ public interface BasicRepository extends IRepository { CompletableFuture> find(); - CompletableFuture findByAAndB(int aa, String b); - - Set find(Set entities); + CompletableFuture findByA(int a); CompletableFuture exists(); - CompletableFuture existsByAOrB(int a, String bb); + CompletableFuture existsByB(String b); void update(List entity); diff --git a/ap/src/test/resources/test/basic/BasicRepositorySqlImpl.java b/ap/src/test/resources/test/basic/BasicRepositorySqlImpl.java index 11ff3ba..be121e3 100644 --- a/ap/src/test/resources/test/basic/BasicRepositorySqlImpl.java +++ b/ap/src/test/resources/test/basic/BasicRepositorySqlImpl.java @@ -11,7 +11,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; -import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -56,12 +55,11 @@ public CompletableFuture> find() { } @Override - public CompletableFuture findByAAndB(int aa, String b) { + public CompletableFuture findByA(int a) { return CompletableFuture.supplyAsync(() -> { try (Connection __connection = this.dataSource.getConnection()) { - try (PreparedStatement __statement = __connection.prepareStatement("select * from hello where a=? and b=?")) { - __statement.setInt(1, aa); - __statement.setString(2, b); + try (PreparedStatement __statement = __connection.prepareStatement("select * from hello where a=?")) { + __statement.setInt(1, a); try (ResultSet __result = __statement.executeQuery()) { if (!__result.next()) { return null; @@ -79,27 +77,6 @@ public CompletableFuture findByAAndB(int aa, String b) { } , this.database.executorService()); } - @Override - public Set find(Set entities) { - try (Connection __connection = this.dataSource.getConnection()) { - try (PreparedStatement __statement = __connection.prepareStatement("select * from hello")) { - try (ResultSet __result = __statement.executeQuery()) { - Set __responses = new java.util.HashSet<>(); - while (__result.next()) { - Integer _a = __result.getInt("a"); - String _b = __result.getString("b"); - String _c = __result.getString("c"); - UUID _d = this.__d.decode(__result.getBytes("d")); - __responses.add(new TestEntity(_a, _b, _c, _d)); - } - return __responses; - } - } - } catch (SQLException __exception) { - throw new CompletionException("Unexpected error occurred", __exception); - } - } - @Override public CompletableFuture exists() { return CompletableFuture.supplyAsync(() -> { @@ -116,12 +93,11 @@ public CompletableFuture exists() { } @Override - public CompletableFuture existsByAOrB(int a, String bb) { + public CompletableFuture existsByB(String b) { return CompletableFuture.supplyAsync(() -> { try (Connection __connection = this.dataSource.getConnection()) { - try (PreparedStatement __statement = __connection.prepareStatement("select 1 from hello where a=? or b=?")) { - __statement.setInt(1, a); - __statement.setString(2, bb); + try (PreparedStatement __statement = __connection.prepareStatement("select 1 from hello where b=?")) { + __statement.setString(1, b); try (ResultSet __result = __statement.executeQuery()) { return __result.next(); }