Skip to content

Repository params with declared mapping should be parsed as simple #157

Merged
merged 2 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,45 +19,49 @@

public final class QueryParameterParser {

public static List<QueryParameter> parse(Types types, ClassName connectionType, ExecutableElement method, ExecutableType methodType) {
return parse(types, List.of(connectionType), method, methodType);
public static List<QueryParameter> parse(Types types, ClassName connectionType, ClassName parameterMapper, ExecutableElement method, ExecutableType methodType) {
return parse(types, List.of(connectionType), parameterMapper, method, methodType);
}

public static List<QueryParameter> parse(Types types, List<ClassName> connectionTypes, ExecutableElement method, ExecutableType methodType) {
public static List<QueryParameter> parse(Types types, List<ClassName> connectionTypes, ClassName parameterMapper, ExecutableElement method, ExecutableType methodType) {
var result = new ArrayList<QueryParameter>(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<ClassName> connectionTypes) {
public static QueryParameter parse(Types types, VariableElement parameter, TypeMirror type, List<ClassName> connectionTypes, ClassName parameterMapper) {
var name = parameter.getSimpleName().toString();
var typeName = TypeName.get(type);
for (var connectionType : connectionTypes) {
if (connectionType.equals(typeName)) {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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")});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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")});
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -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")});
}

}
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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<String>(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]
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>("value")!!
val query = QueryWithParameters.parse(queryString, parameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>("value")!!
val query = QueryWithParameters.parse(queryString, parameters)
Expand Down
Loading