From 7212d054613ad4d50252201fb5be9c53699e42be Mon Sep 17 00:00:00 2001 From: Erin Schnabel Date: Thu, 14 Nov 2019 13:52:11 -0500 Subject: [PATCH] Spring data jpa with MappedSuperclass, fixes #5261 --- .../spring/data/deployment/DotNames.java | 2 + .../data/deployment/MethodNameParser.java | 56 ++++++++++++++++++- .../io/quarkus/it/spring/data/jpa/Book.java | 13 +---- .../it/spring/data/jpa/BookRepository.java | 2 + .../it/spring/data/jpa/BookResource.java | 7 +++ .../quarkus/it/spring/data/jpa/Country.java | 10 +--- .../it/spring/data/jpa/NamedEntity.java | 24 ++++++++ .../it/spring/data/jpa/BookResourceTest.java | 14 +++++ 8 files changed, 106 insertions(+), 22 deletions(-) create mode 100644 integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/NamedEntity.java diff --git a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/DotNames.java b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/DotNames.java index 5eff7b455c3c1..2983624ad868c 100644 --- a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/DotNames.java +++ b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/DotNames.java @@ -10,6 +10,7 @@ import java.util.stream.Stream; import javax.persistence.Id; +import javax.persistence.MappedSuperclass; import org.jboss.jandex.DotName; import org.springframework.data.domain.Page; @@ -63,6 +64,7 @@ public final class DotNames { .createSimple(Modifying.class.getName()); public static final DotName JPA_ID = DotName.createSimple(Id.class.getName()); + public static final DotName JPA_MAPPED_SUPERCLASS = DotName.createSimple(MappedSuperclass.class.getName()); public static final DotName VOID = DotName.createSimple(void.class.getName()); public static final DotName LONG = DotName.createSimple(Long.class.getName()); public static final DotName PRIMITIVE_LONG = DotName.createSimple(long.class.getName()); diff --git a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/MethodNameParser.java b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/MethodNameParser.java index b8899e8cacc29..3b12963cdae36 100644 --- a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/MethodNameParser.java +++ b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/MethodNameParser.java @@ -14,6 +14,9 @@ import org.jboss.jandex.FieldInfo; import org.jboss.jandex.IndexView; import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.ParameterizedType; +import org.jboss.jandex.Type; +import org.jboss.jandex.Type.Kind; import io.quarkus.panache.common.Sort; @@ -57,10 +60,12 @@ public class MethodNameParser { private final ClassInfo entityClass; private final IndexView indexView; + private final List mappedSuperClassInfos; public MethodNameParser(ClassInfo entityClass, IndexView indexView) { this.entityClass = entityClass; this.indexView = indexView; + this.mappedSuperClassInfos = getMappedSuperClassInfos(indexView, entityClass); } public enum QueryType { @@ -185,7 +190,7 @@ public Result parse(MethodInfo methodInfo) { } else { fieldName = lowerFirstLetter(part.replaceAll(operation, "")); } - FieldInfo fieldInfo = entityClass.field(fieldName); + FieldInfo fieldInfo = getField(fieldName); if (fieldInfo == null) { String parsingExceptionMethod = "Entity " + entityClass + " does not contain a field named: " + part + ". " + "Offending method is " + methodName; @@ -213,7 +218,7 @@ public Result parse(MethodInfo methodInfo) { String simpleFieldName = fieldName.substring(0, fieldEndIndex); String associatedEntityFieldName = lowerFirstLetter(fieldName.substring(associatedEntityFieldStartIndex)); - fieldInfo = entityClass.field(simpleFieldName); + fieldInfo = getField(simpleFieldName); if ((fieldInfo == null) || !(fieldInfo.type() instanceof ClassType)) { throw new UnableToParseMethodException(parsingExceptionMethod); } @@ -428,7 +433,52 @@ private String getEntityName() { } private boolean entityContainsField(String fieldName) { - return entityClass.field(fieldName) != null; + if (entityClass.field(fieldName) != null) { + return true; + } + + for (ClassInfo superClass : mappedSuperClassInfos) { + FieldInfo fieldInfo = superClass.field(fieldName); + if (fieldInfo != null) { + return true; + } + } + return false; + } + + private FieldInfo getField(String fieldName) { + FieldInfo fieldInfo = entityClass.field(fieldName); + if (fieldInfo == null) { + for (ClassInfo superClass : mappedSuperClassInfos) { + fieldInfo = superClass.field(fieldName); + if (fieldInfo != null) { + break; + } + } + } + return fieldInfo; + } + + private List getMappedSuperClassInfos(IndexView indexView, ClassInfo entityClass) { + List mappedSuperClassInfos = new ArrayList<>(3); + Type superClassType = entityClass.superClassType(); + while (superClassType != null && !superClassType.name().equals(DotNames.OBJECT)) { + ClassInfo superClass = indexView.getClassByName(entityClass.superName()); + if (superClass.classAnnotation(DotNames.JPA_MAPPED_SUPERCLASS) != null) { + mappedSuperClassInfos.add(superClass); + } + + if (superClassType.kind() == Kind.CLASS) { + superClassType = indexView.getClassByName(superClassType.name()).superClassType(); + } else if (superClassType.kind() == Kind.PARAMETERIZED_TYPE) { + ParameterizedType parameterizedType = superClassType.asParameterizedType(); + superClassType = parameterizedType.owner(); + } + } + if (mappedSuperClassInfos.size() > 0) { + return mappedSuperClassInfos; + } + return Collections.emptyList(); } public static class Result { diff --git a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Book.java b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Book.java index 74b4487842b8f..b6b00ff8171b6 100644 --- a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Book.java +++ b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Book.java @@ -4,19 +4,18 @@ import javax.persistence.Id; @Entity -public class Book { +public class Book extends NamedEntity { private Integer bid; - private String name; private Integer publicationYear; public Book() { } public Book(Integer bid, String name, Integer publicationYear) { + super(name); this.bid = bid; - this.name = name; this.publicationYear = publicationYear; } @@ -29,14 +28,6 @@ public void setBid(Integer bid) { this.bid = bid; } - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - public Integer getPublicationYear() { return publicationYear; } diff --git a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BookRepository.java b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BookRepository.java index 01e2a7abcee44..57dc714d284fa 100644 --- a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BookRepository.java +++ b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BookRepository.java @@ -14,6 +14,8 @@ public interface BookRepository extends Repository { List findAll(); + List findByName(String name); + boolean existsById(Integer id); boolean existsBookByPublicationYearBetween(Integer start, Integer end); diff --git a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BookResource.java b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BookResource.java index 6c7236bba9937..60a6095c385a9 100644 --- a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BookResource.java +++ b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BookResource.java @@ -41,4 +41,11 @@ public boolean existsById(@PathParam("bid") Integer bid) { public boolean existsByPublicationYearBetween(@PathParam("start") Integer start, @PathParam("end") Integer end) { return bookRepository.existsBookByPublicationYearBetween(start, end); } + + @GET + @Path("/name/{name}") + @Produces("application/json") + public List byName(@PathParam("name") String name) { + return bookRepository.findByName(name); + } } diff --git a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Country.java b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Country.java index a7a6787465b65..68ef0609abc28 100644 --- a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Country.java +++ b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Country.java @@ -6,22 +6,20 @@ import javax.persistence.SequenceGenerator; @Entity -public class Country { +public class Country extends NamedEntity { @Id @SequenceGenerator(name = "countrySeqGen", sequenceName = "countrySeq", initialValue = 4, allocationSize = 1) @GeneratedValue(generator = "countrySeqGen") public Long id; - private String name; - private String iso3; public Country() { } public Country(String name, String iso3) { - this.name = name; + super(name); this.iso3 = iso3; } @@ -29,10 +27,6 @@ public Long getId() { return id; } - public String getName() { - return name; - } - public String getIso3() { return iso3; } diff --git a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/NamedEntity.java b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/NamedEntity.java new file mode 100644 index 0000000000000..4d9f11b20305c --- /dev/null +++ b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/NamedEntity.java @@ -0,0 +1,24 @@ +package io.quarkus.it.spring.data.jpa; + +import javax.persistence.MappedSuperclass; + +@MappedSuperclass +public class NamedEntity { + + private String name; + + public NamedEntity() { + } + + public NamedEntity(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/integration-tests/spring-data-jpa/src/test/java/io/quarkus/it/spring/data/jpa/BookResourceTest.java b/integration-tests/spring-data-jpa/src/test/java/io/quarkus/it/spring/data/jpa/BookResourceTest.java index d82300410ac86..813598c36c6f8 100644 --- a/integration-tests/spring-data-jpa/src/test/java/io/quarkus/it/spring/data/jpa/BookResourceTest.java +++ b/integration-tests/spring-data-jpa/src/test/java/io/quarkus/it/spring/data/jpa/BookResourceTest.java @@ -65,4 +65,18 @@ void testNew() { .body(containsString("Upheaval")) .body(containsString("Sapiens")); } + + @Test + void testByName() { + when().get("/book/name/Sapiens").then() + .statusCode(200) + .body(containsString("Sapiens")); + } + + @Test + void testByNameNotFound() { + when().get("/book/name/DoesNotExist").then() + .statusCode(200) + .body("size()", is(0)); + } }