From de4a1322cf060cf8e4a7face846376e74805be65 Mon Sep 17 00:00:00 2001 From: aboyko Date: Thu, 14 Dec 2023 16:35:24 -0500 Subject: [PATCH] Composite key for jps repo with @IdClass --- .../ide/vscode/boot/java/Annotations.java | 6 ++- .../EntityIdForRepoReconciler.java | 13 ++++- .../test/EntityIdForRepoReconcilerTest.java | 49 +++++++++++++++++-- 3 files changed, 61 insertions(+), 7 deletions(-) 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 c5b1be8a1..9483e4547 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 @@ -30,7 +30,11 @@ public class Annotations { public static final String NO_REPO_BEAN = "org.springframework.data.repository.NoRepositoryBean"; 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 JPA_JAVAX_ENTITY_ID = "javax.persistence.Id"; + public static final String JPA_JAKARTA_EMBEDDED_ID = "jakarta.persistence.EmbeddedId"; + public static final String JPA_JAVAX_EMBEDDED_ID = "javax.persistence.EmbeddedId"; + public static final String JPA_JAKARTA_ID_CLASS = "jakarta.persistence.IdClass"; + public static final String JPA_JAVAX_ID_CLASS = "javax.persistence.IdClass"; 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 dbae7dae0..450088d6b 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 @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -331,7 +332,17 @@ public class EntityIdForRepoReconciler implements JdtAstReconciler { private static List findAnnotatedIdTypes(ITypeBinding type, Set visited) { List idTypes = new ArrayList<>(); - List idAnnotations = List.of(Annotations.SPRING_ENTITY_ID, Annotations.JPA_JAKARTA_ENTITY_ID, Annotations.JPA_JAVAX_ENTITY_ID); + for (IAnnotationBinding a : type.getAnnotations()) { + switch (a.getAnnotationType().getQualifiedName()) { + case Annotations.JPA_JAKARTA_ID_CLASS: + case Annotations.JPA_JAVAX_ID_CLASS: + Optional opt = Arrays.stream(a.getAllMemberValuePairs()).filter(p -> "value".equals(p.getName())).map(p -> p.getValue()).findFirst(); + if (opt.isPresent() && opt.get() instanceof ITypeBinding) { + return List.of((ITypeBinding) opt.get()); + } + } + } + List idAnnotations = List.of(Annotations.SPRING_ENTITY_ID, Annotations.JPA_JAKARTA_ENTITY_ID, Annotations.JPA_JAVAX_ENTITY_ID, Annotations.JPA_JAKARTA_EMBEDDED_ID, Annotations.JPA_JAVAX_EMBEDDED_ID); for (IVariableBinding m : type.getDeclaredFields()) { String s = fieldSignature(m); if (!visited.contains(s) && isAnnotationCompatible(m.getAnnotations(), idAnnotations)) { 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 4cba48d44..7def63ec1 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 @@ -1047,16 +1047,15 @@ public class EntityIdForRepoReconcilerTest extends BaseReconcilerTest { } @Test - void compositeId() throws Exception { + void compositeId_1() throws Exception { Path customerSource = createFile("Customer.java", """ package demo; - import org.springframework.data.annotation.Id; + import jakarta.persistence.IdClass; + @IdClass(CustomerId.class) public class Customer { - @Id String id; - @Id String id_additional; } """); @@ -1078,5 +1077,45 @@ public class EntityIdForRepoReconcilerTest extends BaseReconcilerTest { assertEquals(0, problems.size()); } - + + @Test + void compositeId_2() throws Exception { + + Path customerSource = createFile("Customer.java", """ + package demo; + + import jakarta.persistence.IdClass; + import org.springframework.data.annotation.Id; + + @IdClass(CustomerId.class) + public class Customer { + @Id Long id + } + """); + + Path customerId = createFile("CustomerId.java", """ + package demo; + + public record CustomerId(String id) {} + """); + + String source = """ + package demo; + + import org.springframework.data.repository.Repository; + + interface CustomerRepository extends Repository {} + """; + List problems = reconcile("CustomerRepository.java", source, false, customerSource, customerId); + + 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 'demo.CustomerId'", problem.getMessage()); + + } }