Skip to content

Commit 2266350

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 8f4156b commit 2266350

File tree

4 files changed

+99
-39
lines changed

4 files changed

+99
-39
lines changed

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import net.sf.jsqlparser.schema.Column;
2727
import net.sf.jsqlparser.statement.Statement;
2828
import net.sf.jsqlparser.statement.delete.Delete;
29+
import net.sf.jsqlparser.statement.insert.Insert;
2930
import net.sf.jsqlparser.statement.select.OrderByElement;
3031
import net.sf.jsqlparser.statement.select.PlainSelect;
3132
import net.sf.jsqlparser.statement.select.Select;
@@ -82,7 +83,9 @@ private ParsedType detectParsedType() {
8283
try {
8384
Statement statement = CCJSqlParserUtil.parse(this.query.getQueryString());
8485

85-
if (statement instanceof Update) {
86+
if (statement instanceof Insert) {
87+
return ParsedType.INSERT;
88+
} else if (statement instanceof Update) {
8689
return ParsedType.UPDATE;
8790
} else if (statement instanceof Delete) {
8891
return ParsedType.DELETE;
@@ -475,10 +478,11 @@ public DeclaredQuery getQuery() {
475478
* <li>{@code ParsedType.DELETE}: means the top level statement is {@link Delete}</li>
476479
* <li>{@code ParsedType.UPDATE}: means the top level statement is {@link Update}</li>
477480
* <li>{@code ParsedType.SELECT}: means the top level statement is {@link Select}</li>
481+
* <li>{@code ParsedType.INSERT}: means the top level statement is {@link Insert}</li>
478482
* </ul>
479483
*/
480484
enum ParsedType {
481-
DELETE, UPDATE, SELECT;
485+
DELETE, UPDATE, SELECT, INSERT;
482486
}
483487

484488
}

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

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

18-
import static java.util.Arrays.*;
18+
import static java.util.Arrays.asList;
1919
import static org.assertj.core.api.Assertions.*;
20-
import static org.springframework.data.domain.Example.*;
20+
import static org.springframework.data.domain.Example.of;
2121
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.*;
22+
import static org.springframework.data.domain.Sort.Direction.ASC;
23+
import static org.springframework.data.domain.Sort.Direction.DESC;
2424
import static org.springframework.data.jpa.domain.Specification.not;
25+
import static org.springframework.data.jpa.domain.Specification.where;
2526
import static org.springframework.data.jpa.domain.sample.UserSpecifications.*;
2627

2728
import jakarta.persistence.EntityManager;
@@ -33,14 +34,7 @@
3334
import jakarta.persistence.criteria.Root;
3435
import lombok.Data;
3536

36-
import java.util.ArrayList;
37-
import java.util.Collection;
38-
import java.util.Collections;
39-
import java.util.HashSet;
40-
import java.util.List;
41-
import java.util.Map;
42-
import java.util.Optional;
43-
import java.util.Set;
37+
import java.util.*;
4438
import java.util.stream.Stream;
4539

4640
import org.assertj.core.api.SoftAssertions;
@@ -54,14 +48,7 @@
5448
import org.springframework.dao.DataIntegrityViolationException;
5549
import org.springframework.dao.IncorrectResultSizeDataAccessException;
5650
import org.springframework.dao.InvalidDataAccessApiUsageException;
57-
import org.springframework.data.domain.Example;
58-
import org.springframework.data.domain.ExampleMatcher;
59-
import org.springframework.data.domain.Page;
60-
import org.springframework.data.domain.PageImpl;
61-
import org.springframework.data.domain.PageRequest;
62-
import org.springframework.data.domain.Pageable;
63-
import org.springframework.data.domain.Slice;
64-
import org.springframework.data.domain.Sort;
51+
import org.springframework.data.domain.*;
6552
import org.springframework.data.domain.Sort.Direction;
6653
import org.springframework.data.domain.Sort.Order;
6754
import org.springframework.data.jpa.domain.Specification;
@@ -2969,7 +2956,7 @@ void complexWithNativeStatement() {
29692956
}
29702957

29712958
@Test // GH-2607
2972-
void containsWithCollection(){
2959+
void containsWithCollection() {
29732960

29742961
firstUser.getAttributes().add("cool");
29752962
firstUser.getAttributes().add("hip");
@@ -2986,6 +2973,35 @@ void containsWithCollection(){
29862973
assertThat(result).containsOnly(firstUser, secondUser);
29872974
}
29882975

2976+
@Test // GH-2593
2977+
void insertStatementModifyingQueryWorks() {
2978+
flushTestUsers();
2979+
repository.insertNewUserWithNativeQuery();
2980+
2981+
List<User> all = repository.findAll();
2982+
assertThat(all) //
2983+
.isNotNull() //
2984+
.isNotEmpty() //
2985+
.hasSize(5) //
2986+
.map(User::getLastname) //
2987+
.contains("Gierke", "Arrasz", "Matthews", "raymond", "K");
2988+
}
2989+
2990+
@Test // GH-2593
2991+
void insertStatementModifyingQueryWithParamsWorks() {
2992+
flushTestUsers();
2993+
String testLastName = "TestLastName";
2994+
repository.insertNewUserWithParamNativeQuery(testLastName);
2995+
2996+
List<User> all = repository.findAll();
2997+
assertThat(all) //
2998+
.isNotNull() //
2999+
.isNotEmpty() //
3000+
.hasSize(5) //
3001+
.map(User::getLastname) //
3002+
.contains("Gierke", "Arrasz", "Matthews", "raymond", testLastName);
3003+
}
3004+
29893005
private Page<User> executeSpecWithSort(Sort sort) {
29903006

29913007
flushTestUsers();

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

+38
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,44 @@ void multipleWithStatementsWorksWithJSQLParser() {
889889
assertThat(queryEnhancer.hasConstructorExpression()).isFalse();
890890
}
891891

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

894932
return Stream.of( //

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

+18-16
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,14 @@
1818
import jakarta.persistence.EntityManager;
1919
import jakarta.persistence.QueryHint;
2020

21-
import java.util.Collection;
22-
import java.util.Date;
23-
import java.util.List;
24-
import java.util.Map;
25-
import java.util.Optional;
26-
import java.util.Set;
21+
import java.util.*;
2722
import java.util.stream.Stream;
2823

29-
import org.springframework.data.domain.Page;
30-
import org.springframework.data.domain.PageRequest;
31-
import org.springframework.data.domain.Pageable;
32-
import org.springframework.data.domain.Slice;
33-
import org.springframework.data.domain.Sort;
24+
import org.springframework.data.domain.*;
3425
import org.springframework.data.jpa.domain.sample.Role;
3526
import org.springframework.data.jpa.domain.sample.SpecialUser;
3627
import org.springframework.data.jpa.domain.sample.User;
37-
import org.springframework.data.jpa.repository.JpaRepository;
38-
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
39-
import org.springframework.data.jpa.repository.Modifying;
40-
import org.springframework.data.jpa.repository.Query;
41-
import org.springframework.data.jpa.repository.QueryHints;
28+
import org.springframework.data.jpa.repository.*;
4229
import org.springframework.data.jpa.repository.query.Procedure;
4330
import org.springframework.data.repository.CrudRepository;
4431
import org.springframework.data.repository.query.Param;
@@ -55,6 +42,7 @@
5542
* @author JyotirmoyVS
5643
* @author Greg Turnquist
5744
* @author Simon Paradies
45+
* @author Diego Krupitza
5846
*/
5947
public interface UserRepository
6048
extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User>, UserRepositoryCustom {
@@ -679,6 +667,20 @@ List<String> findAllAndSortByFunctionResultNamedParameter(@Param("namedParameter
679667
// GH-2607
680668
List<User> findByAttributesContains(String attribute);
681669

670+
// GH-2593
671+
@Modifying
672+
@Query(
673+
value = "INSERT INTO SD_User(id,active,age,firstname,lastname,emailAddress,DTYPE) VALUES (9999,true,23,'Diego','K','[email protected]','User')",
674+
nativeQuery = true)
675+
void insertNewUserWithNativeQuery();
676+
677+
// GH-2593
678+
@Modifying
679+
@Query(
680+
value = "INSERT INTO SD_User(id,active,age,firstname,lastname,emailAddress,DTYPE) VALUES (9999,true,23,'Diego',:lastname,'[email protected]','User')",
681+
nativeQuery = true)
682+
void insertNewUserWithParamNativeQuery(@Param("lastname") String lastname);
683+
682684
interface RolesAndFirstname {
683685

684686
String getFirstname();

0 commit comments

Comments
 (0)