Skip to content

Commit 77f1cce

Browse files
committed
Data JDBC query support
Signed-off-by: BoykoAlex <alex.boyko@broadcom.com>
1 parent ce0a17c commit 77f1cce

File tree

8 files changed

+351
-16
lines changed

8 files changed

+351
-16
lines changed

headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/semantic/tokens/SemanticTokenData.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2024 Broadcom, Inc.
2+
* Copyright (c) 2024, 2025 Broadcom, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -26,6 +26,10 @@ public SemanticTokenData(int start, int end, String type, String[] modifiers) {
2626
this(new Region(start, end -start), type, modifiers);
2727
}
2828

29+
public static Builder builder(String type) {
30+
return new Builder().withType(type);
31+
}
32+
2933
@Override
3034
public int compareTo(SemanticTokenData o) {
3135
if (range.getOffset() == o.range().getOffset()) {
@@ -64,4 +68,54 @@ public int getEnd() {
6468
return range.getEnd();
6569
}
6670

71+
public static class Builder {
72+
int offset = 0;
73+
String text = "";
74+
String[] modifiers = new String[0];
75+
String type;
76+
77+
public Builder withOffset(int offset) {
78+
this.offset = offset;
79+
return this;
80+
}
81+
82+
public Builder withText(String text) {
83+
this.text = text;
84+
return this;
85+
}
86+
87+
public Builder withType(String type) {
88+
this.type = type;
89+
return this;
90+
}
91+
92+
public Builder withModifiers(String[] modifiers) {
93+
this.modifiers = modifiers;
94+
return this;
95+
}
96+
97+
public Builder addOffset(int offset) {
98+
this.offset += offset;
99+
return this;
100+
}
101+
102+
public Builder addOffset(String space) {
103+
this.offset += space.length();
104+
return this;
105+
}
106+
107+
public Builder space() {
108+
this.offset++;
109+
return this;
110+
}
111+
112+
public Builder withPrevious(SemanticTokenData previous) {
113+
return withOffset(previous.getEnd());
114+
}
115+
116+
public SemanticTokenData build() {
117+
return new SemanticTokenData(new Region(offset, text.length()), type, modifiers);
118+
}
119+
120+
}
67121
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/JdtDataQuerySemanticTokensProvider.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2024 Broadcom, Inc.
2+
* Copyright (c) 2024, 2025 Broadcom, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -111,7 +111,8 @@ public List<SemanticTokenData> computeSemanticTokens(IJavaProject jp, EmbeddedLa
111111

112112
@Override
113113
public boolean isApplicable(IJavaProject project) {
114-
return supportState.isEnabled() && (SpringProjectUtil.hasDependencyStartingWith(project, "spring-data-jpa", null)
114+
return supportState.isEnabled() && (SpringProjectUtil.hasDependencyStartingWith(project, "spring-data-jpa", null)
115+
|| SpringProjectUtil.hasDependencyStartingWith(project, "spring-data-jdbc", null)
115116
|| SpringProjectUtil.hasDependencyStartingWith(project, "jakarta.persistence-api", null)
116117
|| SpringProjectUtil.hasDependencyStartingWith(project, "javax.persistence-api", null));
117118
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/JdtQueryDocHighlightsProvider.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2024 Broadcom, Inc.
2+
* Copyright (c) 2024, 2025 Broadcom, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -79,10 +79,12 @@ public List<DocumentHighlight> getDocHighlights(IJavaProject project, TextDocume
7979
static Annotation findQueryAnnotation(AnnotationHierarchies annotationHierarchies, ASTNode node) {
8080
if (node.getParent() instanceof MemberValuePair pair
8181
&& node.getParent().getParent() instanceof NormalAnnotation na && "value".equals(pair.getName().getIdentifier())
82-
&& JdtQueryVisitorUtils.isQueryAnnotation(annotationHierarchies, na)) {
82+
&& (JdtQueryVisitorUtils.isQueryAnnotation(annotationHierarchies, na)
83+
|| JdtQueryVisitorUtils.isQueryJdbcAnnotation(annotationHierarchies, na))) {
8384
return na;
8485
} else if (node.getParent() instanceof SingleMemberAnnotation sm
85-
&& JdtQueryVisitorUtils.isQueryAnnotation(annotationHierarchies, sm)) {
86+
&& (JdtQueryVisitorUtils.isQueryAnnotation(annotationHierarchies, sm)
87+
|| JdtQueryVisitorUtils.isQueryJdbcAnnotation(annotationHierarchies, sm))) {
8688
return sm;
8789
}
8890
return null;

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/JdtQueryVisitorUtils.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class JdtQueryVisitorUtils {
3232
public record EmbeddedQueryExpression(EmbeddedLanguageSnippet query, boolean isNative) {};
3333

3434
public static EmbeddedQueryExpression extractQueryExpression(AnnotationHierarchies annotationHierarchies, SingleMemberAnnotation a) {
35-
if (isNativeQueryAnnotation(annotationHierarchies, a)) {
35+
if (isNativeQueryAnnotation(annotationHierarchies, a) || isQueryJdbcAnnotation(annotationHierarchies, a)) {
3636
EmbeddedLanguageSnippet expression = EmbeddedLangAstUtils.extractEmbeddedExpression(a.getValue());
3737
return expression == null ? null : new EmbeddedQueryExpression(expression, true);
3838
} else if (isQueryAnnotation(annotationHierarchies, a)) {
@@ -45,7 +45,7 @@ public static EmbeddedQueryExpression extractQueryExpression(AnnotationHierarchi
4545
public static EmbeddedQueryExpression extractQueryExpression(AnnotationHierarchies annotationHierarchies, NormalAnnotation a) {
4646
Expression queryExpression = null;
4747
boolean isNative = false;
48-
if (isNativeQueryAnnotation(annotationHierarchies, a)) {
48+
if (isNativeQueryAnnotation(annotationHierarchies, a) || isQueryJdbcAnnotation(annotationHierarchies, a)) {
4949
for (Object value : a.values()) {
5050
if (value instanceof MemberValuePair) {
5151
MemberValuePair pair = (MemberValuePair) value;
@@ -139,6 +139,13 @@ static boolean isNamedQueryAnnotation(AnnotationHierarchies annotationHierarchie
139139
return false;
140140
}
141141

142+
static boolean isQueryJdbcAnnotation(AnnotationHierarchies annotationHierarchies, Annotation a) {
143+
if (Annotations.DATA_JDBC_QUERY.equals(a.getTypeName().getFullyQualifiedName()) || QUERY.equals(a.getTypeName().getFullyQualifiedName())) {
144+
return annotationHierarchies.isAnnotatedWith(a.resolveAnnotationBinding(), Annotations.DATA_JDBC_QUERY);
145+
}
146+
return false;
147+
}
148+
142149
static boolean isNativeQueryAnnotation(AnnotationHierarchies annotationHierarchies, Annotation a) {
143150
if (NATIVE_QUERY.equals(a.getTypeName().getFullyQualifiedName()) || Annotations.DATA_JPA_NATIVE_QUERY.equals(a.getTypeName().getFullyQualifiedName())) {
144151
IAnnotationBinding type = a.resolveAnnotationBinding();

headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/data/jpa/queries/DataQueryParameterDefinitionProviderTest.java

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2024 Broadcom, Inc.
2+
* Copyright (c) 202, 2025 Broadcom, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -83,8 +83,56 @@ public interface OwnerRepository {
8383
Editor editor = harness.newEditor(LanguageId.JAVA, source, Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/OwnerRepository.java").toUri().toASCIIString());
8484

8585
Range expectedRange = editor.rangeOf("String lastName", "lastName");
86-
Range highlightRange = editor.rangeOf(":1%", "1");
86+
Range highlightRange = editor.rangeOf(":1", "1");
8787
editor.assertGotoDefinition(highlightRange.getStart(), expectedRange, highlightRange);
8888

8989
}
90+
91+
@Test
92+
void parameterNameDefinitionJdbc() throws Exception {
93+
String source = """
94+
package my.package
95+
96+
import org.springframework.data.jdbc.repository.query.Query;
97+
import org.springframework.data.repository.query.Param;
98+
99+
public interface EmployeeRepository {
100+
101+
@Query("SELECT * FROM owner WHERE last_name LIKE concat(:lastName,'%')")
102+
void findByLastName(@Param("lastName") String lastName);
103+
}
104+
""";
105+
106+
Editor editor = harness.newEditor(LanguageId.JAVA, source, Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/EmployeeRepository.java").toUri().toASCIIString());
107+
108+
Range expectedRange = editor.rangeOf("String lastName", "lastName");
109+
Range highlightRange = editor.rangeOf(":lastName", "lastName");
110+
editor.assertGotoDefinition(highlightRange.getStart(), expectedRange, highlightRange);
111+
112+
}
113+
114+
@Test
115+
void parameterOrdinalDefinitionJdbc() throws Exception {
116+
String source = """
117+
package my.package
118+
119+
import org.springframework.data.jdbc.repository.query.Query;
120+
import org.springframework.data.repository.query.Param;
121+
122+
public interface EmployeeRepository {
123+
124+
@Query("SELECT * FROM owner WHERE last_name LIKE concat(?1,'%')")
125+
void findByLastName(@Param("lastName") String lastName);
126+
}
127+
""";
128+
129+
Editor editor = harness.newEditor(LanguageId.JAVA, source, Paths.get(jp.getLocationUri()).resolve("src/main/resource/my/package/EmployeeRepository.java").toUri().toASCIIString());
130+
131+
Range expectedRange = editor.rangeOf("String lastName", "lastName");
132+
Range highlightRange = editor.rangeOf("?1", "1");
133+
editor.assertGotoDefinition(highlightRange.getStart(), expectedRange, highlightRange);
134+
135+
}
136+
137+
90138
}

0 commit comments

Comments
 (0)