Polishing.

Related: #2641.
This commit is contained in:
Greg L. Turnquist
2022-09-27 17:44:42 -05:00
parent 7b69aa6dd5
commit 4b267006aa
4 changed files with 37 additions and 38 deletions

View File

@@ -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<SelectItem> 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);

View File

@@ -3008,15 +3008,17 @@ public class UserRepositoryTests {
flushTestUsers();
Optional<User> 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<User> 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<User> executeSpecWithSort(Sort sort) {

View File

@@ -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<Arguments> 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<Arguments> 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));

View File

@@ -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();