diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Annotations.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Annotations.java index a330e41ab..001df9b11 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Annotations.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Annotations.java @@ -26,7 +26,9 @@ public class Annotations { public static final String REPOSITORY = "org.springframework.stereotype.Repository"; public static final String REPOSITORY_DEFINITION = "org.springframework.data.repository.RepositoryDefinition"; public static final String NO_REPO_BEAN = "org.springframework.data.repository.NoRepositoryBean"; - public static final String ENTITY_ID = "org.springframework.data.annotation.Id"; + public static final String SPRING_ENTITY_ID = "org.springframework.data.annotation.Id"; + public static final String JPA_JAKARTA_ENTITY_ID = "jakarta.persistence.Id"; + public static final String JPA_JAVAX_ENTITY_ID = "javax.persistence.Id"; public static final String AUTOWIRED = "org.springframework.beans.factory.annotation.Autowired"; public static final String INJECT = "javax.inject.Inject"; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/EntityIdForRepoReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/EntityIdForRepoReconciler.java index 8ebfdf940..6476bc427 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/EntityIdForRepoReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/EntityIdForRepoReconciler.java @@ -15,6 +15,7 @@ import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.spri import java.net.URI; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -325,16 +326,17 @@ public class EntityIdForRepoReconciler implements JdtAstReconciler { } private static ITypeBinding findAnnotatedIdType(ITypeBinding type, Set visited) { + List idAnnotations = List.of(Annotations.SPRING_ENTITY_ID, Annotations.JPA_JAKARTA_ENTITY_ID, Annotations.JPA_JAVAX_ENTITY_ID); for (IVariableBinding m : type.getDeclaredFields()) { String s = fieldSignature(m); - if (!visited.contains(s) && isAnnotationCompatible(m.getAnnotations(), Annotations.ENTITY_ID)) { + if (!visited.contains(s) && isAnnotationCompatible(m.getAnnotations(), idAnnotations)) { return m.getType(); } visited.add(s); } for (IMethodBinding m : type.getDeclaredMethods()) { String s = methodSignature(m); - if (!visited.contains(s) && isAnnotationCompatible(m.getAnnotations(), Annotations.ENTITY_ID)) { + if (!visited.contains(s) && isAnnotationCompatible(m.getAnnotations(), idAnnotations)) { return m.getReturnType(); } visited.add(s); @@ -349,10 +351,10 @@ public class EntityIdForRepoReconciler implements JdtAstReconciler { return f.getName(); } - private static boolean isAnnotationCompatible(IAnnotationBinding[] annotations, String fqName) { + private static boolean isAnnotationCompatible(IAnnotationBinding[] annotations, Collection fqNames) { for (IAnnotationBinding a : annotations) { if (AnnotationHierarchies.findTransitiveSuperAnnotationBindings(a) - .anyMatch(at -> fqName.equals(at.getAnnotationType().getQualifiedName()))) { + .anyMatch(at -> fqNames.contains(at.getAnnotationType().getQualifiedName()))) { return true; } } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/EntityIdForRepoReconcilerTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/EntityIdForRepoReconcilerTest.java index 790cd1161..d0006d965 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/EntityIdForRepoReconcilerTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/reconcilers/test/EntityIdForRepoReconcilerTest.java @@ -330,6 +330,42 @@ public class EntityIdForRepoReconcilerTest extends BaseReconcilerTest { } + @Test + void inheritance_invalidDomainId_string_number_jakarta_id() throws Exception { + + Path employeeSource = createFile("Customer.java", """ + package demo; + + import jakarta.persistence.Id; + + public class Customer { + @Id String customerNumber; + } + """); + + String source = """ + package demo; + + import org.springframework.data.repository.Repository; + + interface CustomerRepository extends Repository {} + """; + List problems = reconcile("CustomerRepository.java", source, false, employeeSource); + + assertEquals(1, problems.size()); + + ReconcileProblem problem = problems.get(0); + + assertEquals(Boot2JavaProblemType.DOMAIN_ID_FOR_REPOSITORY, problem.getType()); + + String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength()); + assertEquals("Long", markedStr); + assertEquals("Expected Domain ID type is 'java.lang.String'", problem.getMessage()); + + assertEquals(0, problem.getQuickfixes().size()); + + } + @Test void inheritance_invalidDomainId_string_number_2() throws Exception { @@ -765,7 +801,7 @@ public class EntityIdForRepoReconcilerTest extends BaseReconcilerTest { import org.springframework.data.annotation.Id; - public record Employee(@Id String id) {}; + public record Employee(@Id String employeeId) {}; """); String source = """ @@ -792,6 +828,40 @@ public class EntityIdForRepoReconcilerTest extends BaseReconcilerTest { } + @Test + void invalid_DomainId_record_1() throws Exception { + + Path employeeSource = createFile("Employee.java", """ + package demo; + + import org.springframework.data.annotation.Id; + + public record Employee(@Id String employeeId) {}; + """); + + String source = """ + package demo; + + import org.springframework.data.repository.Repository; + + interface EmployeeRepository extends Repository{} + """; + List problems = reconcile("EmployeeRepository.java", source, false, employeeSource); + + assertEquals(1, problems.size()); + + ReconcileProblem problem = problems.get(0); + + assertEquals(Boot2JavaProblemType.DOMAIN_ID_FOR_REPOSITORY, problem.getType()); + + String markedStr = source.substring(problem.getOffset(), problem.getOffset() + problem.getLength()); + assertEquals("Number", markedStr); + assertEquals("Expected Domain ID type is 'java.lang.String'", problem.getMessage()); + + assertEquals(0, problem.getQuickfixes().size()); + + } + @Test void valid_DomainId_record() throws Exception {