From 4b267006aa93345121cda9feca9b8da94746341d Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Tue, 27 Sep 2022 17:44:42 -0500 Subject: [PATCH] Polishing. Related: #2641. --- .../query/JSqlParserQueryEnhancer.java | 47 +++++++++---------- .../jpa/repository/UserRepositoryTests.java | 10 ++-- .../query/QueryEnhancerUnitTests.java | 10 ++-- .../jpa/repository/sample/UserRepository.java | 8 ++-- 4 files changed, 37 insertions(+), 38 deletions(-) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java index b55e4e2ac..0efa084c1 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java @@ -15,22 +15,9 @@ */ package org.springframework.data.jpa.repository.query; -import static org.springframework.data.jpa.repository.query.JSqlParserUtils.*; -import static org.springframework.data.jpa.repository.query.QueryUtils.*; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import org.springframework.data.domain.Sort; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; +import static org.springframework.data.jpa.repository.query.JSqlParserUtils.getJSqlCount; +import static org.springframework.data.jpa.repository.query.JSqlParserUtils.getJSqlLower; +import static org.springframework.data.jpa.repository.query.QueryUtils.checkSortExpression; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Alias; @@ -42,17 +29,19 @@ import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.merge.Merge; -import net.sf.jsqlparser.statement.select.OrderByElement; -import net.sf.jsqlparser.statement.select.PlainSelect; -import net.sf.jsqlparser.statement.select.Select; -import net.sf.jsqlparser.statement.select.SelectBody; -import net.sf.jsqlparser.statement.select.SelectExpressionItem; -import net.sf.jsqlparser.statement.select.SelectItem; -import net.sf.jsqlparser.statement.select.SetOperationList; -import net.sf.jsqlparser.statement.select.WithItem; +import net.sf.jsqlparser.statement.select.*; import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.statement.values.ValuesStatement; +import java.util.*; +import java.util.stream.Collectors; + +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + /** * The implementation of {@link QueryEnhancer} using JSqlParser. * @@ -147,7 +136,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { /** * Returns the {@link SetOperationList} as a string query with {@link Sort}s applied in the right order. - * + * * @param setOperationListStatement * @param sort * @return @@ -305,14 +294,16 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { private String detectAlias(String query) { if (ParsedType.MERGE.equals(this.parsedType)) { + Merge mergeStatement = parseSelectStatement(query, Merge.class); return detectAlias(mergeStatement); } else if (ParsedType.SELECT.equals(this.parsedType)) { + Select selectStatement = parseSelectStatement(query); /* - For all the other types ({@link ValuesStatement} and {@link SetOperationList}) it does not make sense to provide + For all the other types ({@link ValuesStatement} and {@link SetOperationList}) it does not make sense to provide alias since: * ValuesStatement has no alias * SetOperation can have multiple alias for each operation item @@ -354,6 +345,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { */ @Nullable private String detectAlias(Merge mergeStatement) { + Alias alias = mergeStatement.getUsingAlias(); return alias == null ? null : alias.getName(); } @@ -382,6 +374,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { selectBody.setOrderByElements(null); if (StringUtils.hasText(countProjection)) { + Function jSqlCount = getJSqlCount(Collections.singletonList(countProjection), false); selectBody.setSelectItems(Collections.singletonList(new SelectExpressionItem(jSqlCount))); return selectBody.toString(); @@ -396,6 +389,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { List selectItems = selectBody.getSelectItems(); if (onlyASingleColumnProjection(selectItems)) { + SelectExpressionItem singleProjection = (SelectExpressionItem) selectItems.get(0); Column column = (Column) singleProjection.getExpression(); @@ -440,6 +434,7 @@ public class JSqlParserQueryEnhancer implements QueryEnhancer { SelectBody selectBody = selectStatement.getSelectBody(); if (selectStatement.getSelectBody()instanceof SetOperationList setOperationList) { + // using the first one since for setoperations the projection has to be the same selectBody = setOperationList.getSelects().get(0); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 955dc32da..4d32e8b5c 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -3008,15 +3008,17 @@ public class UserRepositoryTests { flushTestUsers(); - Optional byIdUser = repository.findById(firstUser.getId()); - assertThat(byIdUser).isPresent().map(User::getAge).get().isEqualTo(28); + assertThat(repository.findById(firstUser.getId())) // + .isPresent() // + .map(User::getAge).contains(28); // when repository.mergeNativeStatement(); // then - Optional afterUpdate = repository.findById(firstUser.getId()); - assertThat(afterUpdate).isPresent().map(User::getAge).get().isEqualTo(30); + assertThat(repository.findById(firstUser.getId())) // + .isPresent() // + .map(User::getAge).contains(30); } private Page executeSpecWithSort(Sort sort) { diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java index febfbd947..c082840c2 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java @@ -924,6 +924,7 @@ class QueryEnhancerUnitTests { @ParameterizedTest // GH-2641 @MethodSource("mergeStatementWorksWithJSqlParserSource") void mergeStatementWorksWithJSqlParser(String query, String alias) { + StringQuery stringQuery = new StringQuery(query, true); QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery); @@ -937,6 +938,7 @@ class QueryEnhancerUnitTests { } public static Stream insertStatementIsProcessedSameAsDefaultSource() { + return Stream.of( // Arguments.of("INSERT INTO FOO(A) VALUES('A')"), // Arguments.of("INSERT INTO randomsecondTable(A,B,C,D) VALUES('A','B','C','D')") // @@ -944,9 +946,11 @@ class QueryEnhancerUnitTests { } public static Stream mergeStatementWorksWithJSqlParserSource() { - return Stream.of(Arguments.of( - "merge into a using (select id, value from b) query on (a.id = query.id) when matched then update set a.value = value", - "query"), + + return Stream.of( // + Arguments.of( + "merge into a using (select id, value from b) query on (a.id = query.id) when matched then update set a.value = value", + "query"), Arguments.of( "merge into a using (select id2, value from b) on (id = id2) when matched then update set a.value = value", null)); diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index df67e8beb..7bf350f9f 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -684,11 +684,9 @@ public interface UserRepository // GH-2641 @Modifying(clearAutomatically = true) - @Query(value = "merge into sd_user " + - "using (select id from sd_user where age < 30) request " + - "on (sd_user.id = request.id) " + - "when matched then " + - " update set sd_user.age = 30", + @Query( + value = "merge into sd_user " + "using (select id from sd_user where age < 30) request " + + "on (sd_user.id = request.id) " + "when matched then " + " update set sd_user.age = 30", nativeQuery = true) int mergeNativeStatement();