diff --git a/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/cassandra/CassandraRepositoryGenerator.java b/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/cassandra/CassandraRepositoryGenerator.java index 59ae061e3..b3cf81405 100644 --- a/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/cassandra/CassandraRepositoryGenerator.java +++ b/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/cassandra/CassandraRepositoryGenerator.java @@ -66,7 +66,7 @@ public TypeSpec generate(TypeElement repositoryElement, TypeSpec.Builder type, M var parameterMappers = new FieldFactory(this.types, type, constructor, "_parameter_mapper_"); for (var method : queryMethods) { var methodType = (ExecutableType) this.types.asMemberOf(repositoryType, method); - var parameters = QueryParameterParser.parse(this.types, CassandraTypes.CONNECTION, method, methodType); + var parameters = QueryParameterParser.parse(this.types, CassandraTypes.CONNECTION, CassandraTypes.PARAMETER_COLUMN_MAPPER, method, methodType); var queryAnnotation = CommonUtils.findDirectAnnotation(method, DbUtils.QUERY_ANNOTATION); var queryString = CommonUtils.parseAnnotationValueWithoutDefault(queryAnnotation, "value").toString(); var query = QueryWithParameters.parse(filer, queryString, parameters); diff --git a/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/jdbc/JdbcRepositoryGenerator.java b/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/jdbc/JdbcRepositoryGenerator.java index 224523b1c..a58682c79 100644 --- a/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/jdbc/JdbcRepositoryGenerator.java +++ b/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/jdbc/JdbcRepositoryGenerator.java @@ -3,7 +3,6 @@ import com.squareup.javapoet.*; import reactor.core.publisher.Mono; import ru.tinkoff.kora.annotation.processor.common.CommonUtils; -import ru.tinkoff.kora.annotation.processor.common.MethodUtils; import ru.tinkoff.kora.annotation.processor.common.FieldFactory; import ru.tinkoff.kora.annotation.processor.common.Visitors; import ru.tinkoff.kora.common.Tag; @@ -62,7 +61,7 @@ public TypeSpec generate(TypeElement repositoryElement, TypeSpec.Builder type, M var parameterMappers = new FieldFactory(this.types, type, constructor, "_parameter_mapper_"); for (var method : queryMethods) { var methodType = (ExecutableType) this.types.asMemberOf(repositoryType, method); - var parameters = QueryParameterParser.parse(this.types, JdbcTypes.CONNECTION, method, methodType); + var parameters = QueryParameterParser.parse(this.types, JdbcTypes.CONNECTION, JdbcTypes.PARAMETER_COLUMN_MAPPER, method, methodType); var queryAnnotation = CommonUtils.findDirectAnnotation(method, DbUtils.QUERY_ANNOTATION); var queryString = CommonUtils.parseAnnotationValueWithoutDefault(queryAnnotation, "value").toString(); var query = QueryWithParameters.parse(filer, queryString, parameters); diff --git a/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/model/QueryParameterParser.java b/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/model/QueryParameterParser.java index adacd0385..aef2cc094 100644 --- a/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/model/QueryParameterParser.java +++ b/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/model/QueryParameterParser.java @@ -19,24 +19,20 @@ public final class QueryParameterParser { - public static List parse(Types types, ClassName connectionType, ExecutableElement method, ExecutableType methodType) { - return parse(types, List.of(connectionType), method, methodType); + public static List parse(Types types, ClassName connectionType, ClassName parameterMapper, ExecutableElement method, ExecutableType methodType) { + return parse(types, List.of(connectionType), parameterMapper, method, methodType); } - public static List parse(Types types, List connectionTypes, ExecutableElement method, ExecutableType methodType) { + public static List parse(Types types, List connectionTypes, ClassName parameterMapper, ExecutableElement method, ExecutableType methodType) { var result = new ArrayList(method.getParameters().size()); for (int i = 0; i < method.getParameters().size(); i++) { - var parameter = QueryParameterParser.parse(types, method.getParameters().get(i), methodType.getParameterTypes().get(i), connectionTypes); + var parameter = QueryParameterParser.parse(types, method.getParameters().get(i), methodType.getParameterTypes().get(i), connectionTypes, parameterMapper); result.add(parameter); } return result; } - public static QueryParameter parse(Types types, VariableElement parameter, TypeMirror type, ClassName connectionType) { - return parse(types, parameter, type, List.of(connectionType)); - } - - public static QueryParameter parse(Types types, VariableElement parameter, TypeMirror type, List connectionTypes) { + public static QueryParameter parse(Types types, VariableElement parameter, TypeMirror type, List connectionTypes, ClassName parameterMapper) { var name = parameter.getSimpleName().toString(); var typeName = TypeName.get(type); for (var connectionType : connectionTypes) { @@ -44,20 +40,28 @@ public static QueryParameter parse(Types types, VariableElement parameter, TypeM return new QueryParameter.ConnectionParameter(name, type, parameter); } } + var mapping = CommonUtils.parseMapping(parameter).getMapping(parameterMapper); var batch = CommonUtils.findDirectAnnotation(parameter, DbUtils.BATCH_ANNOTATION); if (batch != null) { if (!(typeName instanceof ParameterizedTypeName ptn && (ptn.rawType.canonicalName().equals("java.util.List")))) { throw new ProcessingErrorException("@Batch parameter must be a list", parameter); } var batchType = ((DeclaredType) type).getTypeArguments().get(0); - var entity = DbEntity.parseEntity(types, batchType); - if (entity != null) { - var param = new QueryParameter.EntityParameter(name, entity, parameter); - return new QueryParameter.BatchParameter(name, type, parameter, param); + final QueryParameter param; + if (mapping != null) { + param = new QueryParameter.SimpleParameter(name, batchType, parameter); } else { - var param = new QueryParameter.SimpleParameter(name, batchType, parameter); - return new QueryParameter.BatchParameter(name, type, parameter, param); + var entity = DbEntity.parseEntity(types, batchType); + if (entity != null) { + param = new QueryParameter.EntityParameter(name, entity, parameter); + } else { + param = new QueryParameter.SimpleParameter(name, batchType, parameter); + } } + return new QueryParameter.BatchParameter(name, type, parameter, param); + } + if (mapping != null) { + return new QueryParameter.SimpleParameter(name, type, parameter); } var entity = DbEntity.parseEntity(types, type); if (entity != null) { diff --git a/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/r2dbc/R2dbcRepositoryGenerator.java b/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/r2dbc/R2dbcRepositoryGenerator.java index 1e07f8c2f..6fbe86c2c 100644 --- a/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/r2dbc/R2dbcRepositoryGenerator.java +++ b/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/r2dbc/R2dbcRepositoryGenerator.java @@ -4,8 +4,6 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import ru.tinkoff.kora.annotation.processor.common.*; -import ru.tinkoff.kora.annotation.processor.common.MethodUtils; -import ru.tinkoff.kora.common.Context; import ru.tinkoff.kora.common.Context; import ru.tinkoff.kora.common.Tag; import ru.tinkoff.kora.database.annotation.processor.DbUtils; @@ -65,7 +63,7 @@ public TypeSpec generate(TypeElement repositoryElement, TypeSpec.Builder type, M for (var method : queryMethods) { var methodType = (ExecutableType) this.types.asMemberOf(repositoryType, method); - var parameters = QueryParameterParser.parse(this.types, R2dbcTypes.CONNECTION, method, methodType); + var parameters = QueryParameterParser.parse(this.types, R2dbcTypes.CONNECTION, R2dbcTypes.PARAMETER_COLUMN_MAPPER, method, methodType); var queryAnnotation = CommonUtils.findDirectAnnotation(method, DbUtils.QUERY_ANNOTATION); var queryString = CommonUtils.parseAnnotationValueWithoutDefault(queryAnnotation, "value").toString(); var query = QueryWithParameters.parse(filer, queryString, parameters); diff --git a/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/vertx/VertxRepositoryGenerator.java b/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/vertx/VertxRepositoryGenerator.java index 5285ebe5b..b20b737b8 100644 --- a/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/vertx/VertxRepositoryGenerator.java +++ b/database/database-annotation-processor/src/main/java/ru/tinkoff/kora/database/annotation/processor/vertx/VertxRepositoryGenerator.java @@ -64,7 +64,7 @@ public TypeSpec generate(TypeElement repositoryElement, TypeSpec.Builder type, M var parameterMappers = new FieldFactory(this.types, type, constructor, "_parameter_mapper_"); for (var method : queryMethods) { var methodType = (ExecutableType) this.types.asMemberOf(repositoryType, method); - var parameters = QueryParameterParser.parse(this.types, List.of(VertxTypes.CONNECTION, VertxTypes.SQL_CLIENT), method, methodType); + var parameters = QueryParameterParser.parse(this.types, List.of(VertxTypes.CONNECTION, VertxTypes.SQL_CLIENT), VertxTypes.PARAMETER_COLUMN_MAPPER, method, methodType); var queryAnnotation = CommonUtils.findDirectAnnotation(method, DbUtils.QUERY_ANNOTATION); var queryString = CommonUtils.parseAnnotationValueWithoutDefault(queryAnnotation, "value").toString(); var query = QueryWithParameters.parse(filer, queryString, parameters); diff --git a/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/cassandra/CassandraParametersTest.java b/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/cassandra/CassandraParametersTest.java index 9608c9b1a..5dae39579 100644 --- a/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/cassandra/CassandraParametersTest.java +++ b/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/cassandra/CassandraParametersTest.java @@ -24,6 +24,7 @@ import ru.tinkoff.kora.database.common.annotation.processor.entity.TestEntityRecord.TestUnknownType; import ru.tinkoff.kora.database.common.annotation.processor.entity.TestEntityRecord.UnknownTypeField; +import java.sql.SQLException; import java.util.List; import java.util.Map; @@ -426,4 +427,24 @@ public interface TestRepository extends CassandraRepository { assertThat(tag).isNotNull(); assertThat(tag.value()).isEqualTo(new Class[]{compileResult.loadClass("TestRepository")}); } + + @Test + public void testRecordParameterMapping() throws ClassNotFoundException, SQLException { + var mapper = Mockito.mock(CassandraParameterColumnMapper.class); + var repository = compileCassandra(List.of(mapper), """ + @Repository + public interface TestRepository extends CassandraRepository { + @Query("INSERT INTO test(value) VALUES (:value::jsonb)") + void test(@Tag(TestRepository.class) TestRecord value); + } + """, """ + public record TestRecord(String value){} + """); + + var mapperConstructorParameter = repository.repositoryClass.getConstructors()[0].getParameters()[1]; + assertThat(mapperConstructorParameter.getType()).isEqualTo(CassandraParameterColumnMapper.class); + var tag = mapperConstructorParameter.getAnnotation(Tag.class); + assertThat(tag).isNotNull(); + assertThat(tag.value()).isEqualTo(new Class[]{compileResult.loadClass("TestRepository")}); + } } diff --git a/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/jdbc/JdbcParametersTest.java b/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/jdbc/JdbcParametersTest.java index f0db68c2f..075330b67 100644 --- a/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/jdbc/JdbcParametersTest.java +++ b/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/jdbc/JdbcParametersTest.java @@ -365,4 +365,24 @@ public interface TestRepository extends JdbcRepository { assertThat(tag.value()).isEqualTo(new Class[]{compileResult.loadClass("TestRepository")}); } + @Test + public void testRecordParameterMapping() throws ClassNotFoundException, SQLException { + var mapper = Mockito.mock(JdbcParameterColumnMapper.class); + var repository = compileJdbc(List.of(mapper), """ + @Repository + public interface TestRepository extends JdbcRepository { + @Query("INSERT INTO test(value) VALUES (:value::jsonb)") + void test(@Tag(TestRepository.class) TestRecord value); + } + """, """ + public record TestRecord(String value){} + """); + + var mapperConstructorParameter = repository.repositoryClass.getConstructors()[0].getParameters()[1]; + assertThat(mapperConstructorParameter.getType()).isEqualTo(JdbcParameterColumnMapper.class); + var tag = mapperConstructorParameter.getAnnotation(Tag.class); + assertThat(tag).isNotNull(); + assertThat(tag.value()).isEqualTo(new Class[]{compileResult.loadClass("TestRepository")}); + } + } diff --git a/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/r2dbc/R2dbcParametersTest.java b/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/r2dbc/R2dbcParametersTest.java index 8ed20971a..a7ef982a9 100644 --- a/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/r2dbc/R2dbcParametersTest.java +++ b/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/r2dbc/R2dbcParametersTest.java @@ -272,5 +272,25 @@ public interface TestRepository extends R2dbcRepository { assertThat(tag.value()).isEqualTo(new Class[]{compileResult.loadClass("TestRepository")}); } + @Test + public void testRecordParameterMappingByTag() throws ClassNotFoundException { + var mapper = Mockito.mock(R2dbcParameterColumnMapper.class); + var repository = compileR2dbc(List.of(mapper), """ + @Repository + public interface TestRepository extends R2dbcRepository { + @Query("INSERT INTO test(value) VALUES (:value)") + void test(@Tag(TestRepository.class) TestRecord value); + } + """, """ + public record TestRecord(String value){} + """); + + var mapperConstructorParameter = repository.repositoryClass.getConstructors()[0].getParameters()[1]; + assertThat(mapperConstructorParameter.getType()).isEqualTo(R2dbcParameterColumnMapper.class); + var tag = mapperConstructorParameter.getAnnotation(Tag.class); + assertThat(tag).isNotNull(); + assertThat(tag.value()).isEqualTo(new Class[]{compileResult.loadClass("TestRepository")}); + } + } diff --git a/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/vertx/VertxParametersTest.java b/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/vertx/VertxParametersTest.java index bbf17c7b7..aae3ad01e 100644 --- a/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/vertx/VertxParametersTest.java +++ b/database/database-annotation-processor/src/test/java/ru/tinkoff/kora/database/common/annotation/processor/vertx/VertxParametersTest.java @@ -260,4 +260,24 @@ public interface TestRepository extends VertxRepository { assertThat(tag.value()).isEqualTo(new Class[]{compileResult.loadClass("TestRepository")}); } + @Test + public void testRecordParameterMappingByTag() { + var mapper = Mockito.mock(VertxParameterColumnMapper.class); + var repository = compileVertx(List.of(mapper), """ + @Repository + public interface TestRepository extends VertxRepository { + @Query("INSERT INTO test(value) VALUES (:value)") + void test(@Tag(TestRepository.class) TestRecord value); + } + """, """ + public record TestRecord(String value){} + """); + + var mapperConstructorParameter = repository.repositoryClass.getConstructors()[0].getParameters()[1]; + assertThat(mapperConstructorParameter.getType()).isEqualTo(VertxParameterColumnMapper.class); + var tag = mapperConstructorParameter.getAnnotation(Tag.class); + assertThat(tag).isNotNull(); + assertThat(tag.value()).isEqualTo(new Class[]{compileResult.loadClass("TestRepository")}); + } + } diff --git a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/EntityUtils.kt b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/EntityUtils.kt index 8886a2e30..95809bd33 100644 --- a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/EntityUtils.kt +++ b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/EntityUtils.kt @@ -1,16 +1,9 @@ package ru.tinkoff.kora.database.symbol.processor -import com.google.devtools.ksp.getConstructors -import com.google.devtools.ksp.isPublic -import com.google.devtools.ksp.symbol.KSClassDeclaration -import com.google.devtools.ksp.symbol.KSFunctionDeclaration -import com.google.devtools.ksp.symbol.KSPropertyDeclaration import com.google.devtools.ksp.symbol.KSValueParameter import ru.tinkoff.kora.common.naming.NameConverter import ru.tinkoff.kora.common.naming.SnakeCaseNameConverter import ru.tinkoff.kora.ksp.common.AnnotationUtils.findAnnotation -import ru.tinkoff.kora.ksp.common.exception.ProcessingError -import ru.tinkoff.kora.ksp.common.exception.ProcessingErrorException import ru.tinkoff.kora.ksp.common.parseAnnotationValue private val defaultColumnNameConverter = SnakeCaseNameConverter() @@ -26,56 +19,3 @@ fun parseColumnName(valueParameter: KSValueParameter, columnsNameConverter: Name } return defaultColumnNameConverter.convert(fieldName) } - -fun parseColumnName(valueParameter: KSPropertyDeclaration, columnsNameConverter: NameConverter?): String { - val column = valueParameter.findAnnotation(DbUtils.columnAnnotation) - if (column != null) { - return parseAnnotationValue(column, "value")!! - } - val fieldName = valueParameter.simpleName.asString() - if (columnsNameConverter != null) { - return columnsNameConverter.convert(fieldName) - } - return defaultColumnNameConverter.convert(fieldName) -} - - -fun findEntityConstructor(declaration: KSClassDeclaration): KSFunctionDeclaration { - val constructors = declaration.getConstructors() - .filter { it.isPublic() } - .toList() - if (constructors.isEmpty()) { - throw ProcessingErrorException( - listOf( - ProcessingError( - "Entity type ${declaration.simpleName.asString()} has no public constructors", declaration - ) - ) - ) - } - if (constructors.size == 1) { - return constructors[0] - } - val entityConstructors = constructors - .filter { c -> c.findAnnotation(DbUtils.entityConstructorAnnotation) != null } - .toList() - if (entityConstructors.isEmpty()) { - throw ProcessingErrorException( - listOf( - ProcessingError( - "Entity type ${declaration.simpleName.asString()} has more than one public constructor and none of them is marked with @EntityConstructor", declaration - ) - ) - ) - } - if (entityConstructors.size != 1) { - throw ProcessingErrorException( - listOf( - ProcessingError( - "Entity type ${declaration.simpleName.asString()} has more than one public constructor and more then one of them is marked with @EntityConstructor", declaration - ) - ) - ) - } - return entityConstructors[0] -} diff --git a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/cassandra/CassandraRepositoryGenerator.kt b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/cassandra/CassandraRepositoryGenerator.kt index 37763ce0e..5324a8914 100644 --- a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/cassandra/CassandraRepositoryGenerator.kt +++ b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/cassandra/CassandraRepositoryGenerator.kt @@ -46,7 +46,7 @@ class CassandraRepositoryGenerator(private val resolver: Resolver) : RepositoryG val parameterMappers = FieldFactory(typeBuilder, constructorBuilder, "_parameter_mapper") for (method in repositoryType.findQueryMethods()) { val methodType = method.asMemberOf(repositoryResolvedType) - val parameters = QueryParameterParser.parse(CassandraTypes.connection, method, methodType) + val parameters = QueryParameterParser.parse(CassandraTypes.connection, CassandraTypes.parameterColumnMapper, method, methodType) val queryAnnotation = method.findAnnotation(DbUtils.queryAnnotation)!! val queryString = queryAnnotation.findValue("value")!! val query = QueryWithParameters.parse(queryString, parameters) diff --git a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/jdbc/JdbcRepositoryGenerator.kt b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/jdbc/JdbcRepositoryGenerator.kt index 0ec602375..0d8648ffd 100644 --- a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/jdbc/JdbcRepositoryGenerator.kt +++ b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/jdbc/JdbcRepositoryGenerator.kt @@ -45,7 +45,7 @@ class JdbcRepositoryGenerator(private val resolver: Resolver) : RepositoryGenera val parameterMappers = FieldFactory(typeBuilder, constructorBuilder, "_parameter_mapper_") for (method in queryMethods) { val methodType = method.asMemberOf(repositoryResolvedType) - val parameters = QueryParameterParser.parse(JdbcTypes.connection, method, methodType) + val parameters = QueryParameterParser.parse(JdbcTypes.connection, JdbcTypes.jdbcParameterColumnMapper, method, methodType) val queryAnnotation = method.findAnnotation(DbUtils.queryAnnotation)!! val queryString = queryAnnotation.findValue("value")!! val query = QueryWithParameters.parse(queryString, parameters) diff --git a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/model/QueryParameterParser.kt b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/model/QueryParameterParser.kt index bafd9fe36..f2e668e7c 100644 --- a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/model/QueryParameterParser.kt +++ b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/model/QueryParameterParser.kt @@ -11,42 +11,51 @@ import ru.tinkoff.kora.database.symbol.processor.DbUtils import ru.tinkoff.kora.ksp.common.AnnotationUtils.findAnnotation import ru.tinkoff.kora.ksp.common.CommonClassNames.isList import ru.tinkoff.kora.ksp.common.exception.ProcessingErrorException +import ru.tinkoff.kora.ksp.common.parseMappingData object QueryParameterParser { - fun parse(connectionType: ClassName, method: KSFunctionDeclaration, methodType: KSFunction): List { - return parse(listOf(connectionType), method, methodType) + fun parse(connectionType: ClassName, parameterMapperType: ClassName, method: KSFunctionDeclaration, methodType: KSFunction): List { + return parse(listOf(connectionType), parameterMapperType, method, methodType) } - fun parse(connectionTypes: List, method: KSFunctionDeclaration, methodType: KSFunction): List { + fun parse(connectionTypes: List, parameterMapperType: ClassName, method: KSFunctionDeclaration, methodType: KSFunction): List { val result = ArrayList(method.parameters.size) for (i in method.parameters.indices) { - val parameter = this.parse(connectionTypes, method.parameters[i], methodType.parameterTypes[i]!!) + val parameter = this.parse(connectionTypes, parameterMapperType, method.parameters[i], methodType.parameterTypes[i]!!) result.add(parameter) } return result } - private fun parse(connectionTypes: List, parameter: KSValueParameter, type: KSType): QueryParameter { + private fun parse(connectionTypes: List, parameterMapperType: ClassName, parameter: KSValueParameter, type: KSType): QueryParameter { val name = parameter.name!!.getShortName(); val typeName = type.toTypeName(); if (connectionTypes.contains(typeName)) { return QueryParameter.ConnectionParameter(name, type, parameter); } val batch = parameter.findAnnotation(DbUtils.batchAnnotation); + val mapping = parameter.parseMappingData().getMapping(parameterMapperType) if (batch != null) { if (typeName !is ParameterizedTypeName || !type.isList()) { throw ProcessingErrorException("@Batch parameter must be a list", parameter); } + val batchType = type.arguments[0].type!!.resolve() - val entity = DbEntity.parseEntity(batchType) - if (entity != null) { - val param = QueryParameter.EntityParameter(name, batchType, parameter, entity) - return QueryParameter.BatchParameter(name, type, parameter, param) + val param = if (mapping != null) { + QueryParameter.SimpleParameter(name, batchType, parameter) } else { - val param = QueryParameter.SimpleParameter(name, batchType, parameter) - return QueryParameter.BatchParameter(name, type, parameter, param) + val entity = DbEntity.parseEntity(batchType) + if (entity != null) { + QueryParameter.EntityParameter(name, batchType, parameter, entity) + } else { + QueryParameter.SimpleParameter(name, batchType, parameter) + } } + return QueryParameter.BatchParameter(name, type, parameter, param) + } + if (mapping != null) { + return QueryParameter.SimpleParameter(name, type, parameter) } val entity = DbEntity.parseEntity(type); if (entity != null) { diff --git a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/r2dbc/R2DbcRepositoryGenerator.kt b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/r2dbc/R2DbcRepositoryGenerator.kt index 264dcab05..13daf0df2 100644 --- a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/r2dbc/R2DbcRepositoryGenerator.kt +++ b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/r2dbc/R2DbcRepositoryGenerator.kt @@ -47,7 +47,7 @@ class R2DbcRepositoryGenerator(val resolver: Resolver) : RepositoryGenerator { val parameterMappers = FieldFactory(typeBuilder, constructorBuilder, "_parameter_mapper_") for (method in repositoryType.findQueryMethods()) { val methodType = method.asMemberOf(repositoryResolvedType) - val parameters = QueryParameterParser.parse(R2dbcTypes.connection, method, methodType) + val parameters = QueryParameterParser.parse(R2dbcTypes.connection, R2dbcTypes.parameterColumnMapper, method, methodType) val queryAnnotation = method.findAnnotation(DbUtils.queryAnnotation)!! val queryString = queryAnnotation.findValue("value")!! val query = QueryWithParameters.parse(queryString, parameters) diff --git a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/vertx/VertxRepositoryGenerator.kt b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/vertx/VertxRepositoryGenerator.kt index e1f26363b..b413f6632 100644 --- a/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/vertx/VertxRepositoryGenerator.kt +++ b/database/database-symbol-processor/src/main/kotlin/ru/tinkoff/kora/database/symbol/processor/vertx/VertxRepositoryGenerator.kt @@ -43,7 +43,7 @@ class VertxRepositoryGenerator(private val resolver: Resolver, private val kspLo val parameterMappers = FieldFactory(typeBuilder, constructorBuilder, "_parameter_mapper_") for (method in repositoryType.findQueryMethods()) { val methodType = method.asMemberOf(repositoryResolvedType) - val parameters = QueryParameterParser.parse(listOf(VertxTypes.sqlConnection, VertxTypes.sqlClient), method, methodType) + val parameters = QueryParameterParser.parse(listOf(VertxTypes.sqlConnection, VertxTypes.sqlClient), VertxTypes.parameterColumnMapper, method, methodType) val queryAnnotation = method.findAnnotation(DbUtils.queryAnnotation)!! val queryString = queryAnnotation.findValue("value")!! val query = QueryWithParameters.parse(queryString, parameters) diff --git a/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/cassandra/CassandraParametersTest.kt b/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/cassandra/CassandraParametersTest.kt index 1ec8c7f5c..22c4cd69c 100644 --- a/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/cassandra/CassandraParametersTest.kt +++ b/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/cassandra/CassandraParametersTest.kt @@ -347,6 +347,28 @@ class CassandraParametersTest : AbstractCassandraRepositoryTest() { assertThat(tag.value.map { it.java }).isEqualTo(listOf(compileResult.loadClass("TestRepository"))) } + @Test + fun testTagOnDataClassParameter() { + val mapper = Mockito.mock(CassandraParameterColumnMapper::class.java) as CassandraParameterColumnMapper + val repository = compile( + listOf(mapper), """ + @Repository + interface TestRepository : CassandraRepository { + @Query("INSERT INTO test(test) VALUES (:value)") + fun test(@Tag(TestRepository::class) value: TestEntity) + } + """.trimIndent(), """ + data class TestEntity(val value: String) + """.trimIndent() + ) + + val mapperConstructorParameter = repository.repositoryClass.constructors.first().parameters[1] + assertThat(mapperConstructorParameter.type.jvmErasure).isEqualTo(CassandraParameterColumnMapper::class) + val tag = mapperConstructorParameter.findAnnotations(Tag::class).first() + assertThat(tag).isNotNull() + assertThat(tag.value.map { it.java }).isEqualTo(listOf(compileResult.loadClass("TestRepository"))) + } + @Test fun testTagOnEntityField() { val mapper = Mockito.mock(CassandraParameterColumnMapper::class.java) as CassandraParameterColumnMapper diff --git a/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/jdbc/JdbcParametersTest.kt b/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/jdbc/JdbcParametersTest.kt index 55ad256b8..38707413e 100644 --- a/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/jdbc/JdbcParametersTest.kt +++ b/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/jdbc/JdbcParametersTest.kt @@ -261,6 +261,28 @@ class JdbcParametersTest : AbstractJdbcRepositoryTest() { Assertions.assertThat(tag.value.map { it.java }).isEqualTo(listOf(compileResult.loadClass("TestRepository"))) } + @Test + fun testTagOnDataClassParameter() { + val mapper = Mockito.mock(JdbcParameterColumnMapper::class.java) as JdbcParameterColumnMapper + val repository = compile( + listOf(mapper), """ + @Repository + interface TestRepository : JdbcRepository { + @Query("INSERT INTO test(test) VALUES (:value)") + fun test(@Tag(TestRepository::class) value: TestEntity) + } + """.trimIndent(), """ + data class TestEntity(val value: String) + """.trimIndent() + ) + + val mapperConstructorParameter = repository.repositoryClass.constructors.first().parameters[1] + Assertions.assertThat(mapperConstructorParameter.type.jvmErasure).isEqualTo(JdbcParameterColumnMapper::class) + val tag = mapperConstructorParameter.findAnnotations(Tag::class).first() + Assertions.assertThat(tag).isNotNull() + Assertions.assertThat(tag.value.map { it.java }).isEqualTo(listOf(compileResult.loadClass("TestRepository"))) + } + @Test fun testTagOnEntityField() { val mapper = Mockito.mock(JdbcParameterColumnMapper::class.java) as JdbcParameterColumnMapper diff --git a/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/r2dbc/R2dbcParametersTest.kt b/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/r2dbc/R2dbcParametersTest.kt index da2654b4b..b317e5d95 100644 --- a/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/r2dbc/R2dbcParametersTest.kt +++ b/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/r2dbc/R2dbcParametersTest.kt @@ -281,6 +281,28 @@ class R2dbcParametersTest : AbstractR2dbcTest() { Assertions.assertThat(tag.value.map { it.java }).isEqualTo(listOf(compileResult.loadClass("TestRepository"))) } + @Test + fun testTagOnDataClassParameter() { + val mapper = Mockito.mock(R2dbcParameterColumnMapper::class.java) as R2dbcParameterColumnMapper + val repository = compile( + listOf(mapper), """ + @Repository + interface TestRepository : R2dbcRepository { + @Query("INSERT INTO test(test) VALUES (:value)") + fun test(@Tag(TestRepository::class) value: TestEntity) + } + """.trimIndent(), """ + data class TestEntity(val value: String) + """.trimIndent() + ) + + val mapperConstructorParameter = repository.repositoryClass.constructors.first().parameters[1] + Assertions.assertThat(mapperConstructorParameter.type.jvmErasure).isEqualTo(R2dbcParameterColumnMapper::class) + val tag = mapperConstructorParameter.findAnnotations(Tag::class).first() + Assertions.assertThat(tag).isNotNull() + Assertions.assertThat(tag.value.map { it.java }).isEqualTo(listOf(compileResult.loadClass("TestRepository"))) + } + @Test fun testTagOnEntityField() { val mapper = Mockito.mock(R2dbcParameterColumnMapper::class.java) as R2dbcParameterColumnMapper diff --git a/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/vertx/VertxParametersTest.kt b/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/vertx/VertxParametersTest.kt index 83bb23b8a..060163470 100644 --- a/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/vertx/VertxParametersTest.kt +++ b/database/database-symbol-processor/src/test/kotlin/ru/tinkoff/kora/database/symbol/processor/vertx/VertxParametersTest.kt @@ -262,7 +262,6 @@ class VertxParametersTest : AbstractVertxRepositoryTest() { ) } - @Test fun testTagOnParameter() { val mapper = Mockito.mock(VertxParameterColumnMapper::class.java) as VertxParameterColumnMapper @@ -287,6 +286,28 @@ class VertxParametersTest : AbstractVertxRepositoryTest() { Assertions.assertThat(tag.value.map { it.java }).isEqualTo(listOf(compileResult.loadClass("TestRepository"))) } + @Test + fun testTagOnDataClassParameter() { + val mapper = Mockito.mock(VertxParameterColumnMapper::class.java) as VertxParameterColumnMapper + val repository = compile( + listOf(mapper), """ + @Repository + interface TestRepository : VertxRepository { + @Query("INSERT INTO test(test) VALUES (:value)") + fun test(@Tag(TestRepository::class) value: TestEntity) + } + """.trimIndent(), """ + data class TestEntity(val value: String) + """.trimIndent() + ) + + val mapperConstructorParameter = repository.repositoryClass.constructors.first().parameters[1] + Assertions.assertThat(mapperConstructorParameter.type.jvmErasure).isEqualTo(VertxParameterColumnMapper::class) + val tag = mapperConstructorParameter.findAnnotations(Tag::class).first() + Assertions.assertThat(tag).isNotNull() + Assertions.assertThat(tag.value.map { it.java }).isEqualTo(listOf(compileResult.loadClass("TestRepository"))) + } + @Test fun testTagOnEntityField() { val mapper = Mockito.mock(VertxParameterColumnMapper::class.java) as VertxParameterColumnMapper