Skip to content

Commit

Permalink
Merge 05f5a6b into e109da5
Browse files Browse the repository at this point in the history
  • Loading branch information
lpandzic committed Feb 19, 2020
2 parents e109da5 + 05f5a6b commit f364b07
Show file tree
Hide file tree
Showing 30 changed files with 1,017 additions and 239 deletions.
41 changes: 41 additions & 0 deletions infobip-spring-data-common/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.infobip</groupId>
<artifactId>infobip-spring-data-querydsl</artifactId>
<version>3.0.1-SNAPSHOT</version>
</parent>

<artifactId>infobip-spring-data-common</artifactId>

<properties>
<!-- DEPENDENCY VERSIONS -->
<mssqlserver.version>1.12.3</mssqlserver.version>
</properties>

<dependencies>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mssqlserver</artifactId>
<version>${mssqlserver.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.infobip.spring.data.jpa;
package com.infobip.spring.data.common;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.infobip.spring.data.jpa;
package com.infobip.spring.data.common;

import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.*;
Expand Down
48 changes: 48 additions & 0 deletions infobip-spring-data-jdbc-annotation-processor/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.infobip</groupId>
<artifactId>infobip-spring-data-querydsl</artifactId>
<version>3.0.1-SNAPSHOT</version>
</parent>

<artifactId>infobip-spring-data-jdbc-annotation-processor</artifactId>

<properties>
<!-- DEPENDENCY VERSIONS -->
<auto-service.version>1.0-rc6</auto-service.version>
<querydsl.version>4.2.3-SNAPSHOT</querydsl.version>
</properties>

<dependencies>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>${auto-service.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-sql-codegen</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package com.infobip.spring.data.jdbc.annotation.processor;

import com.google.common.base.CaseFormat;
import com.querydsl.apt.*;
import com.querydsl.codegen.*;
import com.querydsl.sql.ColumnMetadata;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;

import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.util.Elements;
import java.util.*;
import java.util.stream.Collectors;

class CustomElementHandler extends TypeElementHandler {

private final Elements elements;
private final String defaultSchema;

public CustomElementHandler(Configuration configuration,
ExtendedTypeFactory typeFactory,
TypeMappings typeMappings,
QueryTypeFactory queryTypeFactory,
Elements elements,
RoundEnvironment roundEnv) {
super(configuration, typeFactory, typeMappings, queryTypeFactory);
this.elements = elements;
this.defaultSchema = getDefaultSchema(roundEnv);
}

private String getDefaultSchema(RoundEnvironment roundEnv) {
Set<? extends Element> defaultSchemaElements = roundEnv.getElementsAnnotatedWith(DefaultSchema.class);

if(defaultSchemaElements.isEmpty()) {
return null;
}

if(defaultSchemaElements.size() > 1) {
throw new IllegalArgumentException("found multiple elements with DefaultSchema " + defaultSchemaElements);
}

return defaultSchemaElements.iterator().next().getAnnotation(DefaultSchema.class).value();
}

@Override
public EntityType handleEntityType(TypeElement element) {
EntityType entityType = super.handleEntityType(element);
updateModel(element, entityType);
return entityType;
}

private void updateModel(TypeElement element, EntityType type) {
Map<Object, Object> data = type.getData();
data.put("table", getTableName(type));
getSchema(element, data).ifPresent(schema -> data.put("schema", schema));

Map<String, Integer> fieldNameToIndex = getFieldNameToIndex(type);

type.getProperties()
.forEach(property -> {
property.getData().put("COLUMN", ColumnMetadata.named(getColumnName(property))
.withIndex(fieldNameToIndex.get(property.getName())));
});
}

private Optional<String> getSchema(TypeElement element, Map<Object, Object> data) {
Schema elementSchema = element.getAnnotation(Schema.class);

if(Objects.isNull(elementSchema)) {
return Optional.ofNullable(defaultSchema);
}

return Optional.of(elementSchema.value());
}

private String getTableName(EntityType model) {
String simpleName = simpleNameWithoutPrefix(model.getSimpleName());
String className = model.getPackageName() + "." + simpleName;
return Optional.ofNullable(elements.getTypeElement(className)
.getAnnotation(Table.class))
.map(Table::value)
.orElse(simpleName);
}

private String getColumnName(Property property) {
String name = nameWithoutPrefix(property.getDeclaringType().getPackageName(),
property.getDeclaringType().getSimpleName());
TypeElement parentType = elements.getTypeElement(name);
return parentType.getEnclosedElements()
.stream()
.filter(element -> element instanceof VariableElement)
.map(element -> (VariableElement) element)
.filter(element -> element.getSimpleName().toString().equals(property.getName()))
.filter(element -> element.getAnnotation(Column.class) != null)
.map(element -> element.getAnnotation(Column.class).value())
.findAny()
.orElseGet(() -> CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, property.getName()));
}

private Map<String, Integer> getFieldNameToIndex(EntityType model) {
String name = nameWithoutPrefix(model.getPackageName(), model.getSimpleName());
TypeElement typeElement = elements.getTypeElement(name);
List<? extends Element> fields = typeElement.getEnclosedElements()
.stream()
.filter(element -> element.getKind().equals(ElementKind.FIELD))
.collect(Collectors.toList());

Map<String, Integer> fieldNameToIndex = new HashMap<>();

for (int index = 0; index < fields.size(); index++) {
fieldNameToIndex.put(fields.get(index).getSimpleName().toString(), index + 1);
}
return fieldNameToIndex;
}

private String simpleNameWithoutPrefix(String simpleName) {
return simpleName.substring(1);
}

private String nameWithoutPrefix(String packageName, String simpleName) {
return name(packageName, simpleNameWithoutPrefix(simpleName));
}

private String name(String packageName, String simpleName) {
return packageName + "." + simpleName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.infobip.spring.data.jdbc.annotation.processor;

import com.google.common.base.Function;
import com.mysema.codegen.model.*;
import com.querydsl.codegen.*;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Set;

class CustomExtendedTypeFactory extends com.querydsl.apt.ExtendedTypeFactory {

private final Elements elements;

public CustomExtendedTypeFactory(
ProcessingEnvironment env,
Set<Class<? extends Annotation>> annotations,
TypeMappings typeMappings,
QueryTypeFactory queryTypeFactory,
Function<EntityType, String> variableNameFunction) {
super(env, annotations, typeMappings, queryTypeFactory, variableNameFunction);
this.elements = env.getElementUtils();
}

@Override
protected Type createType(TypeElement typeElement, TypeCategory category,
List<? extends TypeMirror> typeArgs, boolean deep) {
String simpleName = getSimpleName(typeElement, category);
String name = elements.getPackageOf(typeElement) + "." + simpleName;
String packageName = elements.getPackageOf(typeElement).getQualifiedName().toString();
Type[] params = new Type[typeArgs.size()];
for (int i = 0; i < params.length; i++) {
params[i] = getType(typeArgs.get(i), deep);
}
return new SimpleType(category, name, packageName, simpleName, false,
typeElement.getModifiers().contains(Modifier.FINAL), params);
}

private String getSimpleName(TypeElement typeElement, TypeCategory category) {
if (category.equals(TypeCategory.ENTITY)) {
return "Q" + typeElement.getSimpleName().toString();
}

return typeElement.getSimpleName().toString();
}

@Override
public boolean isSimpleTypeEntity(TypeElement typeElement, Class<? extends Annotation> entityAnn) {
return typeElement.getAnnotation(entityAnn) != null
|| typeElement.getEnclosedElements()
.stream()
.anyMatch(element -> element.getAnnotation(entityAnn) != null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.infobip.spring.data.jdbc.annotation.processor;

import java.lang.annotation.*;

@Retention(RetentionPolicy.SOURCE)
@Target({ ElementType.TYPE })
@Documented
public @interface DefaultSchema {

String value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.infobip.spring.data.jdbc.annotation.processor;

import java.lang.annotation.*;

@Retention(RetentionPolicy.SOURCE)
@Target({ ElementType.TYPE })
@Documented
public @interface Schema {

String value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.infobip.spring.data.jdbc.annotation.processor;

import com.google.auto.service.AutoService;
import com.querydsl.apt.*;
import com.querydsl.codegen.*;
import org.springframework.data.annotation.Id;

import javax.annotation.processing.*;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.lang.annotation.*;
import java.util.Set;
import java.util.stream.Collectors;

@SupportedAnnotationTypes("org.springframework.data.annotation.Id")
@AutoService(Processor.class)
public class SpringDataJdbcAnnotationProcessor extends AbstractQuerydslProcessor {

private RoundEnvironment roundEnv;
private CustomExtendedTypeFactory typeFactory;
private Configuration conf;

@Override
protected Configuration createConfiguration(RoundEnvironment roundEnv) {
Class<? extends Annotation> entity = Id.class;
this.roundEnv = roundEnv;
CodegenModule codegenModule = new CodegenModule();
JavaTypeMappings typeMappings = new JavaTypeMappings();
codegenModule.bind(TypeMappings.class, typeMappings);
codegenModule.bind(QueryTypeFactory.class, new QueryTypeFactoryImpl("", "", ""));
SpringDataJdbcConfiguration springDataJdbcConfiguration = new SpringDataJdbcConfiguration(roundEnv,
processingEnv,
entity, null, null,
null, Ignored.class,
typeMappings,
codegenModule);
this.conf = springDataJdbcConfiguration;
return springDataJdbcConfiguration;
}

@Override
protected TypeElementHandler createElementHandler(TypeMappings typeMappings, QueryTypeFactory queryTypeFactory) {
return new CustomElementHandler(conf, typeFactory, typeMappings, queryTypeFactory, processingEnv.getElementUtils(), roundEnv);
}

@Override
protected CustomExtendedTypeFactory createTypeFactory(Set<Class<? extends Annotation>> entityAnnotations,
TypeMappings typeMappings,
QueryTypeFactory queryTypeFactory) {
CustomExtendedTypeFactory customExtendedTypeFactory = new CustomExtendedTypeFactory(processingEnv,
entityAnnotations,
typeMappings,
queryTypeFactory,
conf.getVariableNameFunction());
this.typeFactory = customExtendedTypeFactory;
return customExtendedTypeFactory;
}

protected Set<TypeElement> collectElements() {
return roundEnv.getElementsAnnotatedWith(conf.getEntityAnnotation())
.stream()
.map(Element::getEnclosingElement)
.filter(element -> element instanceof TypeElement)
.map(element -> (TypeElement) element)
.collect(Collectors.toSet());
}

@Override
protected String getClassName(EntityType model) {
return model.getFullName();
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
private @interface Ignored {
}
}

0 comments on commit f364b07

Please sign in to comment.