Skip to content

Commit

Permalink
Added 'return self' support for simple actions
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim203 committed Feb 10, 2024
1 parent c09ed2e commit a436016
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 45 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ The implementation of the interface is automatically generated while compiling.
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
- 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
- support adding every variable of the entity as parameter
- add `save` which either inserts the entity if it's not present or updates the already existing entity
- implementing MongoDB support
- and plenty more

# Supported types
Every database type is responsible for providing support for at least:
- Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package org.geysermc.databaseutils.processor.action;

import com.squareup.javapoet.MethodSpec;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import org.geysermc.databaseutils.processor.info.EntityInfo;
import org.geysermc.databaseutils.processor.type.RepositoryGenerator;
Expand All @@ -38,9 +39,10 @@ final class DeleteAction extends SimpleAction {
protected void addToSingle(
RepositoryGenerator generator,
EntityInfo info,
TypeElement returnType,
VariableElement parameter,
MethodSpec.Builder spec,
boolean async) {
generator.addDelete(info, parameter, spec, async);
generator.addDelete(info, returnType, parameter, spec, async);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package org.geysermc.databaseutils.processor.action;

import com.squareup.javapoet.MethodSpec;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import org.geysermc.databaseutils.processor.info.EntityInfo;
import org.geysermc.databaseutils.processor.type.RepositoryGenerator;
Expand All @@ -38,9 +39,10 @@ final class InsertAction extends SimpleAction {
protected void addToSingle(
RepositoryGenerator generator,
EntityInfo info,
TypeElement returnType,
VariableElement parameter,
MethodSpec.Builder spec,
boolean async) {
generator.addInsert(info, parameter, spec, async);
generator.addInsert(info, returnType, parameter, spec, async);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ protected SimpleAction(String actionType) {
protected abstract void addToSingle(
RepositoryGenerator generator,
EntityInfo info,
TypeElement returnType,
VariableElement parameter,
MethodSpec.Builder spec,
boolean async);
Expand All @@ -56,15 +57,16 @@ public void addTo(
EntityInfo info,
Types typeUtils,
boolean async) {
if (!TypeUtils.isTypeOf(Void.class, returnType)) {
if (!TypeUtils.isTypeOf(Void.class, returnType) && !TypeUtils.isTypeOf(info.className(), returnType)) {
throw new InvalidRepositoryException(
"Expected Void as return type for %s, got %s", element.getSimpleName(), returnType);
"Expected either Void or %s as return type for %s, got %s",
info.className(), element.getSimpleName(), returnType);
}
if (element.getParameters().size() == 1) {
var parameter = element.getParameters().get(0);
if (TypeUtils.isTypeOf(info.className(), parameter.asType())) {
for (RepositoryGenerator generator : generators) {
addToSingle(generator, info, parameter, MethodSpec.overriding(element), async);
addToSingle(generator, info, returnType, parameter, MethodSpec.overriding(element), async);
}
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package org.geysermc.databaseutils.processor.action;

import com.squareup.javapoet.MethodSpec;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import org.geysermc.databaseutils.processor.info.EntityInfo;
import org.geysermc.databaseutils.processor.type.RepositoryGenerator;
Expand All @@ -38,9 +39,10 @@ final class UpdateAction extends SimpleAction {
protected void addToSingle(
RepositoryGenerator generator,
EntityInfo info,
TypeElement returnType,
VariableElement parameter,
MethodSpec.Builder spec,
boolean async) {
generator.addUpdate(info, parameter, spec, async);
generator.addUpdate(info, returnType, parameter, spec, async);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,14 @@ protected void onConstructorBuilder(MethodSpec.Builder builder) {}

public abstract void addDeleteBy(QueryInfo info, MethodSpec.Builder spec, boolean async);

public abstract void addInsert(EntityInfo info, VariableElement parameter, MethodSpec.Builder spec, boolean async);
public abstract void addInsert(
EntityInfo info, TypeElement returnType, VariableElement parameter, MethodSpec.Builder spec, boolean async);

public abstract void addUpdate(EntityInfo info, VariableElement parameter, MethodSpec.Builder spec, boolean async);
public abstract void addUpdate(
EntityInfo info, TypeElement returnType, VariableElement parameter, MethodSpec.Builder spec, boolean async);

public abstract void addDelete(EntityInfo info, VariableElement parameter, MethodSpec.Builder spec, boolean async);
public abstract void addDelete(
EntityInfo info, TypeElement returnType, VariableElement parameter, MethodSpec.Builder spec, boolean async);

public void init(TypeElement superType, EntityInfo entityInfo) {
if (this.typeSpec != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.function.Function;
import java.util.function.Supplier;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import org.geysermc.databaseutils.processor.info.ColumnInfo;
import org.geysermc.databaseutils.processor.info.EntityInfo;
Expand All @@ -68,7 +69,7 @@ protected void onConstructorBuilder(MethodSpec.Builder builder) {
@Override
public void addFindBy(QueryInfo info, MethodSpec.Builder spec, boolean async) {
var query = "select * from %s where %s".formatted(info.tableName(), createWhereFor(info));
addActionedData(spec, async, query, info.variableNames(), info.parameterNames(), null, info::columnFor, () -> {
addActionedQueryData(spec, async, query, info.variableNames(), info.parameterNames(), info::columnFor, () -> {
spec.beginControlFlow("if (!result.next())");
spec.addStatement("return null");
spec.endControlFlow();
Expand Down Expand Up @@ -96,48 +97,72 @@ public void addFindBy(QueryInfo info, MethodSpec.Builder spec, boolean async) {
@Override
public void addExistsBy(QueryInfo info, MethodSpec.Builder spec, boolean async) {
var query = "select 1 from %s where %s".formatted(info.tableName(), createWhereFor(info));
addActionedData(
addActionedQueryData(
spec,
async,
query,
info.variableNames(),
info.parameterNames(),
null,
info::columnFor,
() -> spec.addStatement("return result.next()"));
}

@Override
public void addDeleteBy(QueryInfo info, MethodSpec.Builder spec, boolean async) {
var query = "delete from %s where %s".formatted(info.tableName(), createWhereFor(info));
addActionedData(spec, async, query, info.variableNames(), info.parameterNames(), null, info::columnFor, null);
addActionedUpdateData(
spec,
info.entityType(),
Void.class.getCanonicalName(),
async,
query,
info.variableNames(),
info.parameterNames(),
null,
info::columnFor);
}

@Override
public void addInsert(EntityInfo info, VariableElement parameter, MethodSpec.Builder spec, boolean async) {
public void addInsert(
EntityInfo info,
TypeElement returnType,
VariableElement parameter,
MethodSpec.Builder spec,
boolean async) {
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.name(), columnNames, columnParameters);
addSimple(info, parameter, query, info.columns(), spec, async);
addSimple(info, returnType, parameter, query, info.columns(), spec, async);
}

@Override
public void addUpdate(EntityInfo info, VariableElement parameter, MethodSpec.Builder spec, boolean async) {
public void addUpdate(
EntityInfo info,
TypeElement returnType,
VariableElement parameter,
MethodSpec.Builder spec,
boolean async) {
var query = "update %s set %s where %s".formatted(info.name(), createSetFor(info), createWhereFor(info));
var variables = new ArrayList<>(info.notKeyColumns());
variables.addAll(info.keyColumns());
addSimple(info, parameter, query, variables, spec, async);
addSimple(info, returnType, parameter, query, variables, spec, async);
}

@Override
public void addDelete(EntityInfo info, VariableElement parameter, MethodSpec.Builder spec, boolean async) {
public void addDelete(
EntityInfo info,
TypeElement returnType,
VariableElement parameter,
MethodSpec.Builder spec,
boolean async) {
var query = "delete from %s where %s".formatted(info.name(), createWhereFor(info));
addSimple(info, parameter, query, info.keyColumns(), spec, async);
addSimple(info, returnType, parameter, query, info.keyColumns(), spec, async);
}

private void addSimple(
EntityInfo info,
TypeElement returnType,
VariableElement parameter,
String query,
List<ColumnInfo> variables,
Expand All @@ -146,7 +171,55 @@ private void addSimple(
var variableNames =
variables.stream().map(column -> column.name().toString()).toList();
var variableFormat = parameter.getSimpleName() + ".%s()";
addActionedData(spec, async, query, variableNames, null, variableFormat, info::columnFor, null);
addActionedUpdateData(
spec,
info.className(),
returnType.getQualifiedName(),
async,
query,
variableNames,
List.of(parameter.getSimpleName()),
variableFormat,
info::columnFor);
}

private void addActionedQueryData(
MethodSpec.Builder spec,
boolean async,
String query,
List<? extends CharSequence> variableNames,
List<? extends CharSequence> parameterNames,
Function<CharSequence, ColumnInfo> columnFor,
Runnable content) {
addActionedData(spec, async, query, variableNames, parameterNames, null, columnFor, () -> {
spec.beginControlFlow("try ($T result = statement.executeQuery())", ResultSet.class);
content.run();
spec.endControlFlow();
});
}

private void addActionedUpdateData(
MethodSpec.Builder spec,
CharSequence entityType,
CharSequence returnType,
boolean async,
String query,
List<? extends CharSequence> variableNames,
List<? extends CharSequence> parameterNames,
String variableFormat,
Function<CharSequence, ColumnInfo> columnFor) {
addActionedData(spec, async, query, variableNames, parameterNames, variableFormat, columnFor, () -> {
spec.addStatement("statement.executeUpdate()");
if (TypeUtils.isTypeOf(Void.class, returnType)) {
spec.addStatement("return null");
} else if (TypeUtils.isTypeOf(entityType, returnType)) {
// todo support also creating an entity type from the given parameters
spec.addStatement("return $L", parameterNames.get(0));
} else {
throw new InvalidRepositoryException(
"Return type can be either void or %s but got %s", entityType, returnType);
}
});
}

private void addActionedData(
Expand Down Expand Up @@ -175,14 +248,7 @@ private void addActionedData(
spec.addStatement(jdbcSetFor(columnType, "statement.%s($L, $L)"), i + 1, input);
}

if (content != null) {
spec.beginControlFlow("try ($T result = statement.executeQuery())", ResultSet.class);
content.run();
spec.endControlFlow();
} else {
spec.addStatement("statement.executeUpdate()");
spec.addStatement("return null");
}
content.run();

spec.endControlFlow();
spec.nextControlFlow("catch ($T exception)", SQLException.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public static boolean isTypeOf(Class<?> clazz, TypeElement element) {
return isTypeOf(clazz, element.getQualifiedName());
}

public static boolean isTypeOf(Class<?> clazz, Name canonicalName) {
return canonicalName.contentEquals(clazz.getCanonicalName());
public static boolean isTypeOf(Class<?> clazz, CharSequence canonicalName) {
return clazz.getCanonicalName().contentEquals(canonicalName);
}

public static boolean isTypeOf(CharSequence name, TypeElement element) {
Expand All @@ -57,6 +57,10 @@ public static boolean isTypeOf(CharSequence expected, TypeMirror actual) {
return isTypeOf(expected, MoreTypes.asTypeElement(actual));
}

public static boolean isTypeOf(CharSequence expected, CharSequence actual) {
return CharSequence.compare(expected, actual) == 0;
}

public static String packageNameFor(Name className) {
return packageNameFor(className.toString());
}
Expand Down
2 changes: 1 addition & 1 deletion ap/src/test/resources/test/basic/BasicRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface BasicRepository extends IRepository<TestEntity> {

CompletableFuture<Boolean> existsByAOrB(int a, String bb);

CompletableFuture<Void> update(TestEntity entity);
TestEntity update(TestEntity entity);

CompletableFuture<Void> insert(TestEntity entity);

Expand Down
26 changes: 12 additions & 14 deletions ap/src/test/resources/test/basic/BasicRepositorySqlImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,19 @@ public CompletableFuture<Boolean> existsByAOrB(int a, String bb) {
}

@Override
public CompletableFuture<Void> update(TestEntity entity) {
return CompletableFuture.supplyAsync(() -> {
try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement statement = connection.prepareStatement("update hello set c=?,d=? where a=? and b=?")) {
statement.setString(1, entity.c());
statement.setBytes(2, this.__d.encode(entity.d()));
statement.setInt(3, entity.a());
statement.setString(4, entity.b());
statement.executeUpdate();
return null;
}
} catch (SQLException exception) {
throw new CompletionException("Unexpected error occurred", exception);
public TestEntity update(TestEntity entity) {
try (Connection connection = dataSource.getConnection()) {
try (PreparedStatement statement = connection.prepareStatement("update hello set c=?,d=? where a=? and b=?")) {
statement.setString(1, entity.c());
statement.setBytes(2, this.__d.encode(entity.d()));
statement.setInt(3, entity.a());
statement.setString(4, entity.b());
statement.executeUpdate();
return entity;
}
} , this.database.executorService());
} catch (SQLException exception) {
throw new CompletionException("Unexpected error occurred", exception);
}
}

@Override
Expand Down

0 comments on commit a436016

Please sign in to comment.