Skip to content

Commit

Permalink
Merge pull request #25925 from geoand/#25920
Browse files Browse the repository at this point in the history
Make REST Data Panache work properly with id fields not named 'id'
  • Loading branch information
geoand authored Jun 3, 2022
2 parents 07ddfcb + a4e7159 commit c025477
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

public class EntityClassHelper {

private static final DotName JAVAX_PERSISTENCE_ID = DotName.createSimple(Id.class.getName());
private final IndexView index;

public EntityClassHelper(IndexView index) {
Expand All @@ -29,7 +30,7 @@ private FieldInfo getIdField(ClassInfo classInfo) {
ClassInfo tmpClassInfo = classInfo;
while (tmpClassInfo != null) {
for (FieldInfo field : tmpClassInfo.fields()) {
if (field.hasAnnotation(DotName.createSimple(Id.class.getName()))) {
if (field.hasAnnotation(JAVAX_PERSISTENCE_ID)) {
return field;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
package io.quarkus.hibernate.orm.rest.data.panache.deployment.entity;

import javax.json.bind.annotation.JsonbTransient;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

import io.quarkus.hibernate.orm.panache.PanacheEntityBase;

@Entity
public class EmptyListItem extends AbstractItem<Long> {
public class EmptyListItem extends PanacheEntityBase {

@Id
@GeneratedValue
private Long cid;

public String name;

@ManyToOne(optional = false)
public Collection collection;

public Long getCid() {
return cid;
}

public void setCid(Long cid) {
this.cid = cid;
}

@JsonbTransient // Avoid infinite loop when serializing
public Collection getCollection() {
return collection;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
Expand Down Expand Up @@ -117,19 +120,48 @@ private RuntimeValue<GetterAccessorsContainer> implementPathParameterValueGetter
for (LinkInfo linkInfo : linkInfos) {
String entityType = linkInfo.getEntityType();
for (String parameterName : linkInfo.getPathParameters()) {
DotName className = DotName.createSimple(entityType);
FieldInfoSupplier byParamName = new FieldInfoSupplier(c -> c.field(parameterName), className, index);

// We implement a getter inside a class that has the required field.
// We later map that getter's accessor with an entity type.
// If a field is inside a parent class, the getter accessor will be mapped to each subclass which
// has REST links that need access to that field.
FieldInfo fieldInfo = getFieldInfo(index, DotName.createSimple(entityType), parameterName);

FieldInfo fieldInfo = byParamName.get();
if ((fieldInfo == null) && parameterName.equals("id")) {
// this is a special case where we want to go through the fields of the class
// and see if any is annotated with any sort of @Id annotation
// N.B. as this module does not depend on any other module that could supply this @Id annotation
// (like Panache), we need this general lookup
FieldInfoSupplier byIdAnnotation = new FieldInfoSupplier(
c -> {
for (FieldInfo field : c.fields()) {
List<AnnotationInstance> annotationInstances = field.annotations();
for (AnnotationInstance annotationInstance : annotationInstances) {
if (annotationInstance.name().toString().endsWith("persistence.Id")) {
return field;
}
}
}
return null;
},
className,
index);
fieldInfo = byIdAnnotation.get();
}
if (fieldInfo == null) {
throw new RuntimeException(
String.format("Class '%s' field '%s' was not found", className, parameterName));
}
GetterMetadata getterMetadata = new GetterMetadata(fieldInfo);
if (!implementedGetters.contains(getterMetadata)) {
implementGetterWithAccessor(classOutput, bytecodeTransformersProducer, getterMetadata);
implementedGetters.add(getterMetadata);
}

getterAccessorsContainerRecorder.addAccessor(getterAccessorsContainer,
entityType, parameterName, getterMetadata.getGetterAccessorName());
entityType, getterMetadata.getFieldName(), getterMetadata.getGetterAccessorName());
}
}
}
Expand All @@ -148,22 +180,37 @@ private void implementGetterWithAccessor(ClassOutput classOutput,
getterAccessorImplementor.implement(classOutput, getterMetadata);
}

/**
* Find a field info by name inside a class.
* This is a recursive method that looks through the class hierarchy until the field throws an error if it's not.
*/
private FieldInfo getFieldInfo(IndexView index, DotName className, String fieldName) {
ClassInfo classInfo = index.getClassByName(className);
if (classInfo == null) {
throw new RuntimeException(String.format("Class '%s' was not found", className));
private static class FieldInfoSupplier implements Supplier<FieldInfo> {

private final Function<ClassInfo, FieldInfo> strategy;
private final DotName className;
private final IndexView index;

public FieldInfoSupplier(Function<ClassInfo, FieldInfo> strategy, DotName className, IndexView index) {
this.strategy = strategy;
this.className = className;
this.index = index;
}
FieldInfo fieldInfo = classInfo.field(fieldName);
if (fieldInfo != null) {
return fieldInfo;

@Override
public FieldInfo get() {
return findFieldRecursively(className);
}
if (classInfo.superName() != null && !classInfo.superName().equals(OBJECT_NAME)) {
return getFieldInfo(index, classInfo.superName(), fieldName);

private FieldInfo findFieldRecursively(DotName className) {
ClassInfo classInfo = index.getClassByName(className);
if (classInfo == null) {
throw new RuntimeException(String.format("Class '%s' was not found", className));
}
FieldInfo result = strategy.apply(classInfo);
if (result != null) {
return result;
}
if (classInfo.superName() != null && !classInfo.superName().equals(OBJECT_NAME)) {
return findFieldRecursively(classInfo.superName());
}
return null;
}
throw new RuntimeException(String.format("Class '%s' field '%s' was not found", className, fieldName));
}

}

0 comments on commit c025477

Please sign in to comment.