From db7121338d09eff1c9f5dba361b897ae80670d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Fri, 26 Aug 2022 09:07:27 +0200 Subject: [PATCH] Create simple query builder in DAO classes --- .../bartlomiejpluta/base/lib/db/Relop.java | 17 +++ .../DataAccessObjectCodeGenerator.kt | 103 +++++++++++++++++- 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 api/src/main/java/com/bartlomiejpluta/base/lib/db/Relop.java diff --git a/api/src/main/java/com/bartlomiejpluta/base/lib/db/Relop.java b/api/src/main/java/com/bartlomiejpluta/base/lib/db/Relop.java new file mode 100644 index 00000000..96c5df04 --- /dev/null +++ b/api/src/main/java/com/bartlomiejpluta/base/lib/db/Relop.java @@ -0,0 +1,17 @@ +package com.bartlomiejpluta.base.lib.db; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum Relop { + EQ("="), + NE("<>"), + GT(">"), + GE(">="), + LT("<"), + LE("<="); + + @Getter + private final String op; +} diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/generator/DataAccessObjectCodeGenerator.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/generator/DataAccessObjectCodeGenerator.kt index 3991eee4..3d01e393 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/generator/DataAccessObjectCodeGenerator.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/build/generator/DataAccessObjectCodeGenerator.kt @@ -154,7 +154,11 @@ class DataAccessObjectCodeGenerator : CodeGenerator { MethodSpec.methodBuilder("find") .addParameter( ParameterSpec.builder(dbToJavaType(primaryKey), "id") - .addAnnotation(ClassName.get("lombok", "NonNull")) + .apply { + if (!dbToJavaType(primaryKey).isPrimitive) { + addAnnotation(ClassName.get("lombok", "NonNull")) + } + } .build() ) .returns(model) @@ -179,6 +183,13 @@ class DataAccessObjectCodeGenerator : CodeGenerator { .endControlFlow(")") .build() ) + .addMethod( + MethodSpec.methodBuilder("query") + .addModifiers(Modifier.PUBLIC) + .returns(QUERY_BUILDER_CLASS) + .addStatement("return new \$T()", QUERY_BUILDER_CLASS) + .build() + ) .addMethod( MethodSpec.methodBuilder("save") .addParameter( @@ -277,6 +288,86 @@ class DataAccessObjectCodeGenerator : CodeGenerator { .endControlFlow(")") .build() ) + .addType(TypeSpec.enumBuilder(COLUMN_ENUM) + .addAnnotation(REQUIRED_ARGS_CONSTRUCTOR_ANNOTATION) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .apply { + table.columns.forEach { + addEnumConstant( + it.name.uppercase(), + TypeSpec.anonymousClassBuilder("\$S", it.name).build() + ) + } + } + .addField(String::class.java, "column", Modifier.PRIVATE, Modifier.FINAL) + .build()) + .addType( + TypeSpec.classBuilder(QUERY_FILTER_CLASS) + .addModifiers(Modifier.PRIVATE, Modifier.STATIC) + .addAnnotation(REQUIRED_ARGS_CONSTRUCTOR_ANNOTATION) + .addField(COLUMN_ENUM, "column", Modifier.PRIVATE, Modifier.FINAL) + .addField(RELOP_ENUM, "op", Modifier.PRIVATE, Modifier.FINAL) + .addField(Object::class.java, "value", Modifier.PRIVATE, Modifier.FINAL) + .build() + ) + .addType( + TypeSpec.classBuilder(QUERY_BUILDER_CLASS) + .addAnnotation(PRIVATE_REQUIRED_ARGS_CONSTRUCTOR_ANNOTATION) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) + .addField( + FieldSpec.builder( + ParameterizedTypeName.get(ClassName.get(List::class.java), QUERY_FILTER_CLASS), + "filters", + Modifier.PRIVATE, + Modifier.FINAL + ) + .initializer("new \$T<>()", LinkedList::class.java).build() + ) + .addMethod( + MethodSpec.methodBuilder("where") + .addModifiers(Modifier.PUBLIC) + .addParameter(COLUMN_ENUM, "column") + .addParameter(RELOP_ENUM, "op") + .addParameter(Object::class.java, "value") + .returns(QUERY_BUILDER_CLASS) + .addStatement("this.filters.add(new \$T(column, op, value))", QUERY_FILTER_CLASS) + .addStatement("return this") + .build() + ) + .addMethod( + MethodSpec.methodBuilder("find") + .addModifiers(Modifier.PUBLIC) + .returns(ParameterizedTypeName.get(ClassName.get(List::class.java), model)) + .addStatement("var list = new \$T<\$T>()", LinkedList::class.java, model) + .beginControlFlow("\$T.INSTANCE.getContext().withDatabase(db ->", CONTEXT_HOLDER) + .addStatement( + "var filter = filters.stream().map(f -> String.format(\"`%s` %s ?\", f.column.column, f.op.getOp())).collect(\$T.joining(\" AND \"))", + COLLECTORS_CLASS + ) + .apply { + val sql = "SELECT * FROM `${table.name}` WHERE " + addStatement("var statement = db.prepareStatement(\"$sql\" + filter)") + } + .addStatement("var i = 1") + .beginControlFlow("for (var f : filters)") + .addStatement("statement.setObject(i++, f.value)") + .endControlFlow() + .addStatement("var result = statement.executeQuery()") + .beginControlFlow("while(result.next())") + .addStatement("var model = ${model.simpleName()}.builder()") + .apply { + table.columns.forEach { column -> + addStatement("model.${snakeToCamelCase(column.name)}(result.${dbToGetMethod(column)}(\"${column.name}\"))") + } + } + .addStatement("list.add(model.build())") + .endControlFlow() + .endControlFlow(")") + .addStatement("return list") + .build() + ) + .build() + ) .build() JavaFile @@ -336,7 +427,17 @@ class DataAccessObjectCodeGenerator : CodeGenerator { private val BUILDER_ANNOTATION = ClassName.get("lombok", "Builder") private val DATA_ANNOTATION = ClassName.get("lombok", "Data") + private val REQUIRED_ARGS_CONSTRUCTOR_ANNOTATION = ClassName.get("lombok", "RequiredArgsConstructor") + private val PRIVATE_REQUIRED_ARGS_CONSTRUCTOR_ANNOTATION = + AnnotationSpec.builder(ClassName.get("lombok", "RequiredArgsConstructor")) + .addMember("access", "\$T.PRIVATE", ClassName.get("lombok", "AccessLevel")) + .build() private val CONTEXT_HOLDER = ClassName.get("com.bartlomiejpluta.base.api.context", "ContextHolder") + private val COLUMN_ENUM = ClassName.get("", "Column") + private val RELOP_ENUM = ClassName.get("com.bartlomiejpluta.base.lib.db", "Relop") + private val QUERY_FILTER_CLASS = ClassName.get("", "QueryFilter") + private val QUERY_BUILDER_CLASS = ClassName.get("", "QueryBuilder") + private val COLLECTORS_CLASS = ClassName.get("java.util.stream", "Collectors") } } \ No newline at end of file