Skip to content

Commit eff3803

Browse files
DiegoKrupitzagregturn
authored andcommitted
Made JSqlParserQueryEnhancer aware of INSERT statements.
The `detectParsedType()` inside `JSqlParserQueryEnhancer` is now aware of `INSERT` statements, which means `INSERT` statements can now be used in native queries. Closes #2593
1 parent d64cfd1 commit eff3803

File tree

4 files changed

+119
-15
lines changed

4 files changed

+119
-15
lines changed

src/main/java/org/springframework/data/jpa/repository/query/JSqlParserQueryEnhancer.java

+11-6
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import static org.springframework.data.jpa.repository.query.JSqlParserUtils.*;
19-
import static org.springframework.data.jpa.repository.query.QueryUtils.*;
18+
import static org.springframework.data.jpa.repository.query.JSqlParserUtils.getJSqlCount;
19+
import static org.springframework.data.jpa.repository.query.JSqlParserUtils.getJSqlLower;
20+
import static org.springframework.data.jpa.repository.query.QueryUtils.checkSortExpression;
2021

2122
import net.sf.jsqlparser.JSQLParserException;
2223
import net.sf.jsqlparser.expression.Alias;
@@ -26,6 +27,7 @@
2627
import net.sf.jsqlparser.schema.Column;
2728
import net.sf.jsqlparser.statement.Statement;
2829
import net.sf.jsqlparser.statement.delete.Delete;
30+
import net.sf.jsqlparser.statement.insert.Insert;
2931
import net.sf.jsqlparser.statement.select.OrderByElement;
3032
import net.sf.jsqlparser.statement.select.PlainSelect;
3133
import net.sf.jsqlparser.statement.select.Select;
@@ -82,7 +84,9 @@ private ParsedType detectParsedType() {
8284
try {
8385
Statement statement = CCJSqlParserUtil.parse(this.query.getQueryString());
8486

85-
if (statement instanceof Update) {
87+
if (statement instanceof Insert) {
88+
return ParsedType.INSERT;
89+
} else if (statement instanceof Update) {
8690
return ParsedType.UPDATE;
8791
} else if (statement instanceof Delete) {
8892
return ParsedType.DELETE;
@@ -141,7 +145,7 @@ public String applySorting(Sort sort, @Nullable String alias) {
141145

142146
/**
143147
* Returns the {@link SetOperationList} as a string query with {@link Sort}s applied in the right order.
144-
*
148+
*
145149
* @param setOperationListStatement
146150
* @param sort
147151
* @return
@@ -306,7 +310,7 @@ private String detectAlias(String query) {
306310
Select selectStatement = parseSelectStatement(query);
307311

308312
/*
309-
For all the other types ({@link ValuesStatement} and {@link SetOperationList}) it does not make sense to provide
313+
For all the other types ({@link ValuesStatement} and {@link SetOperationList}) it does not make sense to provide
310314
alias since:
311315
* ValuesStatement has no alias
312316
* SetOperation can have multiple alias for each operation item
@@ -478,10 +482,11 @@ public DeclaredQuery getQuery() {
478482
* <li>{@code ParsedType.DELETE}: means the top level statement is {@link Delete}</li>
479483
* <li>{@code ParsedType.UPDATE}: means the top level statement is {@link Update}</li>
480484
* <li>{@code ParsedType.SELECT}: means the top level statement is {@link Select}</li>
485+
* <li>{@code ParsedType.INSERT}: means the top level statement is {@link Insert}</li>
481486
* </ul>
482487
*/
483488
enum ParsedType {
484-
DELETE, UPDATE, SELECT;
489+
DELETE, UPDATE, SELECT, INSERT;
485490
}
486491

487492
}

src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java

+49-7
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,23 @@
1515
*/
1616
package org.springframework.data.jpa.repository;
1717

18-
import static java.util.Arrays.*;
19-
import static org.assertj.core.api.Assertions.*;
20-
import static org.springframework.data.domain.Example.*;
21-
import static org.springframework.data.domain.ExampleMatcher.*;
22-
import static org.springframework.data.domain.Sort.Direction.*;
23-
import static org.springframework.data.jpa.domain.Specification.*;
18+
import static java.util.Arrays.asList;
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
21+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
22+
import static org.springframework.data.domain.Example.of;
23+
import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher;
24+
import static org.springframework.data.domain.ExampleMatcher.StringMatcher;
25+
import static org.springframework.data.domain.ExampleMatcher.matching;
26+
import static org.springframework.data.domain.Sort.Direction.ASC;
27+
import static org.springframework.data.domain.Sort.Direction.DESC;
2428
import static org.springframework.data.jpa.domain.Specification.not;
25-
import static org.springframework.data.jpa.domain.sample.UserSpecifications.*;
29+
import static org.springframework.data.jpa.domain.Specification.where;
30+
import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasAgeLess;
31+
import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstname;
32+
import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasFirstnameLike;
33+
import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasLastname;
34+
import static org.springframework.data.jpa.domain.sample.UserSpecifications.userHasLastnameLikeWithSort;
2635

2736
import lombok.Data;
2837

@@ -2768,6 +2777,39 @@ void complexWithNativeStatement() {
27682777
assertThat(foundData).containsExactly("joachim", "dave", "kevin");
27692778
}
27702779

2780+
@Test // GH-2593
2781+
void insertStatementModifyingQueryWorks() {
2782+
2783+
flushTestUsers();
2784+
2785+
repository.insertNewUserWithNativeQuery();
2786+
2787+
List<User> all = repository.findAll();
2788+
assertThat(all) //
2789+
.isNotNull() //
2790+
.isNotEmpty() //
2791+
.hasSize(5) //
2792+
.map(User::getLastname) //
2793+
.contains("Gierke", "Arrasz", "Matthews", "raymond", "K");
2794+
}
2795+
2796+
@Test // GH-2593
2797+
void insertStatementModifyingQueryWithParamsWorks() {
2798+
2799+
flushTestUsers();
2800+
2801+
String testLastName = "TestLastName";
2802+
repository.insertNewUserWithParamNativeQuery(testLastName);
2803+
2804+
List<User> all = repository.findAll();
2805+
assertThat(all) //
2806+
.isNotNull() //
2807+
.isNotEmpty() //
2808+
.hasSize(5) //
2809+
.map(User::getLastname) //
2810+
.contains("Gierke", "Arrasz", "Matthews", "raymond", testLastName);
2811+
}
2812+
27712813
private Page<User> executeSpecWithSort(Sort sort) {
27722814

27732815
flushTestUsers();

src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerUnitTests.java

+41-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import static org.assertj.core.api.Assertions.*;
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
20+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1921

2022
import java.util.Arrays;
2123
import java.util.Collections;
@@ -889,6 +891,44 @@ void multipleWithStatementsWorksWithJSQLParser() {
889891
assertThat(queryEnhancer.hasConstructorExpression()).isFalse();
890892
}
891893

894+
@ParameterizedTest // GH-2593
895+
@MethodSource("insertStatementIsProcessedSameAsDefaultSource")
896+
void insertStatementIsProcessedSameAsDefault(String insertQuery) {
897+
898+
StringQuery stringQuery = new StringQuery(insertQuery, true);
899+
QueryEnhancer queryEnhancer = QueryEnhancerFactory.forQuery(stringQuery);
900+
901+
Sort sorting = Sort.by("day").descending();
902+
903+
// queryutils results
904+
String queryUtilsDetectAlias = QueryUtils.detectAlias(insertQuery);
905+
String queryUtilsProjection = QueryUtils.getProjection(insertQuery);
906+
String queryUtilsCountQuery = QueryUtils.createCountQueryFor(insertQuery);
907+
Set<String> queryUtilsOuterJoinAlias = QueryUtils.getOuterJoinAliases(insertQuery);
908+
909+
// direct access
910+
assertThat(stringQuery.getAlias()).isEqualToIgnoringCase(queryUtilsDetectAlias);
911+
assertThat(stringQuery.getProjection()).isEqualToIgnoringCase(queryUtilsProjection);
912+
assertThat(stringQuery.hasConstructorExpression()).isFalse();
913+
914+
// access over enhancer
915+
assertThat(queryEnhancer.createCountQueryFor()).isEqualToIgnoringCase(queryUtilsCountQuery);
916+
assertThat(queryEnhancer.applySorting(sorting)).isEqualTo(insertQuery); // cant check with queryutils result since
917+
// query utils appens order by which is not
918+
// supported by sql standard.
919+
assertThat(queryEnhancer.getJoinAliases()).isEqualTo(queryUtilsOuterJoinAlias);
920+
assertThat(queryEnhancer.detectAlias()).isEqualToIgnoringCase(queryUtilsDetectAlias);
921+
assertThat(queryEnhancer.getProjection()).isEqualToIgnoringCase(queryUtilsProjection);
922+
assertThat(queryEnhancer.hasConstructorExpression()).isFalse();
923+
}
924+
925+
public static Stream<Arguments> insertStatementIsProcessedSameAsDefaultSource() {
926+
return Stream.of( //
927+
Arguments.of("INSERT INTO FOO(A) VALUES('A')"), //
928+
Arguments.of("INSERT INTO randomsecondTable(A,B,C,D) VALUES('A','B','C','D')") //
929+
);
930+
}
931+
892932
public static Stream<Arguments> detectsJoinAliasesCorrectlySource() {
893933

894934
return Stream.of( //

src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.util.Set;
2424
import java.util.stream.Stream;
2525

26-
import javax.persistence.EntityManager;
2726
import javax.persistence.QueryHint;
2827

2928
import org.springframework.data.domain.Page;
@@ -55,6 +54,7 @@
5554
* @author JyotirmoyVS
5655
* @author Greg Turnquist
5756
* @author Simon Paradies
57+
* @author Diego Krupitza
5858
*/
5959
public interface UserRepository
6060
extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User>, UserRepositoryCustom {
@@ -676,6 +676,23 @@ List<String> findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter
676676
nativeQuery = true)
677677
List<String> complexWithNativeStatement();
678678

679+
// GH-2607
680+
List<User> findByAttributesContains(String attribute);
681+
682+
// GH-2593
683+
@Modifying
684+
@Query(
685+
value = "INSERT INTO SD_User(id,active,age,firstname,lastname,emailAddress,DTYPE) VALUES (9999,true,23,'Diego','K','[email protected]','User')",
686+
nativeQuery = true)
687+
void insertNewUserWithNativeQuery();
688+
689+
// GH-2593
690+
@Modifying
691+
@Query(
692+
value = "INSERT INTO SD_User(id,active,age,firstname,lastname,emailAddress,DTYPE) VALUES (9999,true,23,'Diego',:lastname,'[email protected]','User')",
693+
nativeQuery = true)
694+
void insertNewUserWithParamNativeQuery(@Param("lastname") String lastname);
695+
679696
interface RolesAndFirstname {
680697

681698
String getFirstname();

0 commit comments

Comments
 (0)